diff --git a/config/identical-files.json b/config/identical-files.json index 4fdac5c7fea..29601d19510 100644 --- a/config/identical-files.json +++ b/config/identical-files.json @@ -143,6 +143,11 @@ "cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll", "csharp/ql/src/semmle/code/csharp/ir/implementation/Opcode.qll" ], + "IR SSASanity": [ + "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSASanity.qll", + "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSASanity.qll", + "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSASanity.qll" + ], "C++ IR InstructionImports": [ "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/InstructionImports.qll", "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/InstructionImports.qll", diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll index e10e266b2ab..f9efad42fb0 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll @@ -13,14 +13,20 @@ IRUserVariable getIRUserVariable(Language::Function func, Language::Variable var } /** - * Represents a variable referenced by the IR for a function. The variable may - * be a user-declared variable (`IRUserVariable`) or a temporary variable - * generated by the AST-to-IR translation (`IRTempVariable`). + * A variable referenced by the IR for a function. The variable may be a user-declared variable + * (`IRUserVariable`) or a temporary variable generated by the AST-to-IR translation + * (`IRTempVariable`). */ -abstract class IRVariable extends TIRVariable { +class IRVariable extends TIRVariable { Language::Function func; - abstract string toString(); + IRVariable() { + this = TIRUserVariable(_, _, func) or + this = TIRTempVariable(func, _, _, _) or + this = TIRStringLiteral(func, _, _, _) + } + + string toString() { none() } /** * Holds if this variable's value cannot be changed within a function. Currently used for string @@ -41,19 +47,19 @@ abstract class IRVariable extends TIRVariable { /** * Gets the type of the variable. */ - abstract Language::LanguageType getLanguageType(); + Language::LanguageType getLanguageType() { none() } /** * Gets the AST node that declared this variable, or that introduced this * variable as part of the AST-to-IR translation. */ - abstract Language::AST getAST(); + Language::AST getAST() { none() } /** * Gets an identifier string for the variable. This identifier is unique * within the function. */ - abstract string getUniqueId(); + string getUniqueId() { none() } /** * Gets the source location of this variable. @@ -72,7 +78,7 @@ abstract class IRVariable extends TIRVariable { } /** - * Represents a user-declared variable referenced by the IR for a function. + * A user-declared variable referenced by the IR for a function. */ class IRUserVariable extends IRVariable, TIRUserVariable { Language::Variable var; @@ -97,20 +103,34 @@ class IRUserVariable extends IRVariable, TIRUserVariable { } /** - * Represents a variable (user-declared or temporary) that is allocated on the - * stack. This includes all parameters, non-static local variables, and - * temporary variables. + * A variable (user-declared or temporary) that is allocated on the stack. This includes all + * parameters, non-static local variables, and temporary variables. */ -abstract class IRAutomaticVariable extends IRVariable { } +class IRAutomaticVariable extends IRVariable { + IRAutomaticVariable() { + exists(Language::Variable var | + this = TIRUserVariable(var, _, func) and + Language::isVariableAutomatic(var) + ) + or + this = TIRTempVariable(func, _, _, _) + } +} +/** + * A user-declared variable that is allocated on the stack. This includes all parameters and + * non-static local variables. + */ class IRAutomaticUserVariable extends IRUserVariable, IRAutomaticVariable { override Language::AutomaticVariable var; - IRAutomaticUserVariable() { Language::isVariableAutomatic(var) } - final override Language::AutomaticVariable getVariable() { result = var } } +/** + * A user-declared variable that is not allocated on the stack. This includes all global variables, + * namespace-scope variables, static fields, and static local variables. + */ class IRStaticUserVariable extends IRUserVariable { override Language::StaticVariable var; @@ -119,10 +139,19 @@ class IRStaticUserVariable extends IRUserVariable { final override Language::StaticVariable getVariable() { result = var } } -abstract class IRGeneratedVariable extends IRVariable { +/** + * A variable that is not user-declared. This includes temporary variables generated as part of IR + * construction, as well as string literals. + */ +class IRGeneratedVariable extends IRVariable { Language::AST ast; Language::LanguageType type; + IRGeneratedVariable() { + this = TIRTempVariable(func, ast, _, type) or + this = TIRStringLiteral(func, ast, type, _) + } + final override Language::LanguageType getLanguageType() { result = type } final override Language::AST getAST() { result = ast } @@ -144,6 +173,11 @@ IRTempVariable getIRTempVariable(Language::AST ast, TempVariableTag tag) { result.getTag() = tag } +/** + * A temporary variable introduced by IR construction. The most common examples are the variable + * generated to hold the return value of afunction, or the variable generated to hold the result of + * a condition operator (`a ? b : c`). + */ class IRTempVariable extends IRGeneratedVariable, IRAutomaticVariable, TIRTempVariable { TempVariableTag tag; @@ -158,18 +192,28 @@ class IRTempVariable extends IRGeneratedVariable, IRAutomaticVariable, TIRTempVa override string getBaseString() { result = "#temp" } } +/** + * A temporary variable generated to hold the return value of a function. + */ class IRReturnVariable extends IRTempVariable { IRReturnVariable() { tag = ReturnValueTempVar() } final override string toString() { result = "#return" } } +/** + * A temporary variable generated to hold the exception thrown by a `ThrowValue` instruction. + */ class IRThrowVariable extends IRTempVariable { IRThrowVariable() { tag = ThrowTempVar() } override string getBaseString() { result = "#throw" } } +/** + * A variable generated to represent the contents of a string literal. This variable acts much like + * a read-only global variable. + */ class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral { Language::StringLiteral literal; 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 e3c78f476e9..8819826d5c3 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 @@ -94,12 +94,21 @@ module InstructionSanity { /** * Holds if instruction `instr` has multiple operands with tag `tag`. */ - query predicate duplicateOperand(Instruction instr, OperandTag tag) { - strictcount(NonPhiOperand operand | - operand = instr.getAnOperand() and - operand.getOperandTag() = tag - ) > 1 and - not tag instanceof UnmodeledUseOperandTag + query predicate duplicateOperand( + Instruction instr, string message, IRFunction func, string funcText + ) { + exists(OperandTag tag, int operandCount | + operandCount = strictcount(NonPhiOperand operand | + operand = instr.getAnOperand() and + operand.getOperandTag() = tag + ) and + operandCount > 1 and + not tag instanceof UnmodeledUseOperandTag and + message = "Instruction has " + operandCount + " operands with tag '" + tag.toString() + "'" + + " in function '$@'." and + func = instr.getEnclosingIRFunction() and + funcText = Language::getIdentityString(func.getFunction()) + ) } /** 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 6ecfd1470bb..bc67acc1be2 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 @@ -6,26 +6,6 @@ private import semmle.code.cpp.models.interfaces.Alias 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. */ @@ -137,7 +117,11 @@ private predicate operandIsPropagated(Operand operand, IntValue bitOffset) { 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. 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 a789edc7590..96b4c28db82 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 @@ -865,3 +865,17 @@ private module CachedForDebugging { result.getTag() = var.getTag() } } + +module SSASanity { + query predicate multipleOperandMemoryLocations( + OldIR::MemoryOperand operand, string message, OldIR::IRFunction func, string funcText + ) { + exists(int locationCount | + locationCount = strictcount(Alias::getOperandMemoryLocation(operand)) and + locationCount > 1 and + func = operand.getEnclosingIRFunction() and + funcText = Language::getIdentityString(func.getFunction()) and + message = "Operand has " + locationCount.toString() + " memory accesses in function '$@'." + ) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSASanity.ql b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSASanity.ql new file mode 100644 index 00000000000..3bd709a93dc --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSASanity.ql @@ -0,0 +1,8 @@ +/** + * @name Aliased SSA Sanity Check + * @description Performs sanity checks on the SSA construction. This query should have no results. + * @kind table + * @id cpp/aliased-ssa-sanity-check + */ + +import SSASanity diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSASanity.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSASanity.qll new file mode 100644 index 00000000000..95e8443b2a3 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSASanity.qll @@ -0,0 +1,2 @@ +private import SSAConstruction as SSA +import SSA::SSASanity diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll index e10e266b2ab..f9efad42fb0 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll @@ -13,14 +13,20 @@ IRUserVariable getIRUserVariable(Language::Function func, Language::Variable var } /** - * Represents a variable referenced by the IR for a function. The variable may - * be a user-declared variable (`IRUserVariable`) or a temporary variable - * generated by the AST-to-IR translation (`IRTempVariable`). + * A variable referenced by the IR for a function. The variable may be a user-declared variable + * (`IRUserVariable`) or a temporary variable generated by the AST-to-IR translation + * (`IRTempVariable`). */ -abstract class IRVariable extends TIRVariable { +class IRVariable extends TIRVariable { Language::Function func; - abstract string toString(); + IRVariable() { + this = TIRUserVariable(_, _, func) or + this = TIRTempVariable(func, _, _, _) or + this = TIRStringLiteral(func, _, _, _) + } + + string toString() { none() } /** * Holds if this variable's value cannot be changed within a function. Currently used for string @@ -41,19 +47,19 @@ abstract class IRVariable extends TIRVariable { /** * Gets the type of the variable. */ - abstract Language::LanguageType getLanguageType(); + Language::LanguageType getLanguageType() { none() } /** * Gets the AST node that declared this variable, or that introduced this * variable as part of the AST-to-IR translation. */ - abstract Language::AST getAST(); + Language::AST getAST() { none() } /** * Gets an identifier string for the variable. This identifier is unique * within the function. */ - abstract string getUniqueId(); + string getUniqueId() { none() } /** * Gets the source location of this variable. @@ -72,7 +78,7 @@ abstract class IRVariable extends TIRVariable { } /** - * Represents a user-declared variable referenced by the IR for a function. + * A user-declared variable referenced by the IR for a function. */ class IRUserVariable extends IRVariable, TIRUserVariable { Language::Variable var; @@ -97,20 +103,34 @@ class IRUserVariable extends IRVariable, TIRUserVariable { } /** - * Represents a variable (user-declared or temporary) that is allocated on the - * stack. This includes all parameters, non-static local variables, and - * temporary variables. + * A variable (user-declared or temporary) that is allocated on the stack. This includes all + * parameters, non-static local variables, and temporary variables. */ -abstract class IRAutomaticVariable extends IRVariable { } +class IRAutomaticVariable extends IRVariable { + IRAutomaticVariable() { + exists(Language::Variable var | + this = TIRUserVariable(var, _, func) and + Language::isVariableAutomatic(var) + ) + or + this = TIRTempVariable(func, _, _, _) + } +} +/** + * A user-declared variable that is allocated on the stack. This includes all parameters and + * non-static local variables. + */ class IRAutomaticUserVariable extends IRUserVariable, IRAutomaticVariable { override Language::AutomaticVariable var; - IRAutomaticUserVariable() { Language::isVariableAutomatic(var) } - final override Language::AutomaticVariable getVariable() { result = var } } +/** + * A user-declared variable that is not allocated on the stack. This includes all global variables, + * namespace-scope variables, static fields, and static local variables. + */ class IRStaticUserVariable extends IRUserVariable { override Language::StaticVariable var; @@ -119,10 +139,19 @@ class IRStaticUserVariable extends IRUserVariable { final override Language::StaticVariable getVariable() { result = var } } -abstract class IRGeneratedVariable extends IRVariable { +/** + * A variable that is not user-declared. This includes temporary variables generated as part of IR + * construction, as well as string literals. + */ +class IRGeneratedVariable extends IRVariable { Language::AST ast; Language::LanguageType type; + IRGeneratedVariable() { + this = TIRTempVariable(func, ast, _, type) or + this = TIRStringLiteral(func, ast, type, _) + } + final override Language::LanguageType getLanguageType() { result = type } final override Language::AST getAST() { result = ast } @@ -144,6 +173,11 @@ IRTempVariable getIRTempVariable(Language::AST ast, TempVariableTag tag) { result.getTag() = tag } +/** + * A temporary variable introduced by IR construction. The most common examples are the variable + * generated to hold the return value of afunction, or the variable generated to hold the result of + * a condition operator (`a ? b : c`). + */ class IRTempVariable extends IRGeneratedVariable, IRAutomaticVariable, TIRTempVariable { TempVariableTag tag; @@ -158,18 +192,28 @@ class IRTempVariable extends IRGeneratedVariable, IRAutomaticVariable, TIRTempVa override string getBaseString() { result = "#temp" } } +/** + * A temporary variable generated to hold the return value of a function. + */ class IRReturnVariable extends IRTempVariable { IRReturnVariable() { tag = ReturnValueTempVar() } final override string toString() { result = "#return" } } +/** + * A temporary variable generated to hold the exception thrown by a `ThrowValue` instruction. + */ class IRThrowVariable extends IRTempVariable { IRThrowVariable() { tag = ThrowTempVar() } override string getBaseString() { result = "#throw" } } +/** + * A variable generated to represent the contents of a string literal. This variable acts much like + * a read-only global variable. + */ class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral { Language::StringLiteral literal; 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 e3c78f476e9..8819826d5c3 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 @@ -94,12 +94,21 @@ module InstructionSanity { /** * Holds if instruction `instr` has multiple operands with tag `tag`. */ - query predicate duplicateOperand(Instruction instr, OperandTag tag) { - strictcount(NonPhiOperand operand | - operand = instr.getAnOperand() and - operand.getOperandTag() = tag - ) > 1 and - not tag instanceof UnmodeledUseOperandTag + query predicate duplicateOperand( + Instruction instr, string message, IRFunction func, string funcText + ) { + exists(OperandTag tag, int operandCount | + operandCount = strictcount(NonPhiOperand operand | + operand = instr.getAnOperand() and + operand.getOperandTag() = tag + ) and + operandCount > 1 and + not tag instanceof UnmodeledUseOperandTag and + message = "Instruction has " + operandCount + " operands with tag '" + tag.toString() + "'" + + " in function '$@'." and + func = instr.getEnclosingIRFunction() and + funcText = Language::getIdentityString(func.getFunction()) + ) } /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll index e10e266b2ab..f9efad42fb0 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll @@ -13,14 +13,20 @@ IRUserVariable getIRUserVariable(Language::Function func, Language::Variable var } /** - * Represents a variable referenced by the IR for a function. The variable may - * be a user-declared variable (`IRUserVariable`) or a temporary variable - * generated by the AST-to-IR translation (`IRTempVariable`). + * A variable referenced by the IR for a function. The variable may be a user-declared variable + * (`IRUserVariable`) or a temporary variable generated by the AST-to-IR translation + * (`IRTempVariable`). */ -abstract class IRVariable extends TIRVariable { +class IRVariable extends TIRVariable { Language::Function func; - abstract string toString(); + IRVariable() { + this = TIRUserVariable(_, _, func) or + this = TIRTempVariable(func, _, _, _) or + this = TIRStringLiteral(func, _, _, _) + } + + string toString() { none() } /** * Holds if this variable's value cannot be changed within a function. Currently used for string @@ -41,19 +47,19 @@ abstract class IRVariable extends TIRVariable { /** * Gets the type of the variable. */ - abstract Language::LanguageType getLanguageType(); + Language::LanguageType getLanguageType() { none() } /** * Gets the AST node that declared this variable, or that introduced this * variable as part of the AST-to-IR translation. */ - abstract Language::AST getAST(); + Language::AST getAST() { none() } /** * Gets an identifier string for the variable. This identifier is unique * within the function. */ - abstract string getUniqueId(); + string getUniqueId() { none() } /** * Gets the source location of this variable. @@ -72,7 +78,7 @@ abstract class IRVariable extends TIRVariable { } /** - * Represents a user-declared variable referenced by the IR for a function. + * A user-declared variable referenced by the IR for a function. */ class IRUserVariable extends IRVariable, TIRUserVariable { Language::Variable var; @@ -97,20 +103,34 @@ class IRUserVariable extends IRVariable, TIRUserVariable { } /** - * Represents a variable (user-declared or temporary) that is allocated on the - * stack. This includes all parameters, non-static local variables, and - * temporary variables. + * A variable (user-declared or temporary) that is allocated on the stack. This includes all + * parameters, non-static local variables, and temporary variables. */ -abstract class IRAutomaticVariable extends IRVariable { } +class IRAutomaticVariable extends IRVariable { + IRAutomaticVariable() { + exists(Language::Variable var | + this = TIRUserVariable(var, _, func) and + Language::isVariableAutomatic(var) + ) + or + this = TIRTempVariable(func, _, _, _) + } +} +/** + * A user-declared variable that is allocated on the stack. This includes all parameters and + * non-static local variables. + */ class IRAutomaticUserVariable extends IRUserVariable, IRAutomaticVariable { override Language::AutomaticVariable var; - IRAutomaticUserVariable() { Language::isVariableAutomatic(var) } - final override Language::AutomaticVariable getVariable() { result = var } } +/** + * A user-declared variable that is not allocated on the stack. This includes all global variables, + * namespace-scope variables, static fields, and static local variables. + */ class IRStaticUserVariable extends IRUserVariable { override Language::StaticVariable var; @@ -119,10 +139,19 @@ class IRStaticUserVariable extends IRUserVariable { final override Language::StaticVariable getVariable() { result = var } } -abstract class IRGeneratedVariable extends IRVariable { +/** + * A variable that is not user-declared. This includes temporary variables generated as part of IR + * construction, as well as string literals. + */ +class IRGeneratedVariable extends IRVariable { Language::AST ast; Language::LanguageType type; + IRGeneratedVariable() { + this = TIRTempVariable(func, ast, _, type) or + this = TIRStringLiteral(func, ast, type, _) + } + final override Language::LanguageType getLanguageType() { result = type } final override Language::AST getAST() { result = ast } @@ -144,6 +173,11 @@ IRTempVariable getIRTempVariable(Language::AST ast, TempVariableTag tag) { result.getTag() = tag } +/** + * A temporary variable introduced by IR construction. The most common examples are the variable + * generated to hold the return value of afunction, or the variable generated to hold the result of + * a condition operator (`a ? b : c`). + */ class IRTempVariable extends IRGeneratedVariable, IRAutomaticVariable, TIRTempVariable { TempVariableTag tag; @@ -158,18 +192,28 @@ class IRTempVariable extends IRGeneratedVariable, IRAutomaticVariable, TIRTempVa override string getBaseString() { result = "#temp" } } +/** + * A temporary variable generated to hold the return value of a function. + */ class IRReturnVariable extends IRTempVariable { IRReturnVariable() { tag = ReturnValueTempVar() } final override string toString() { result = "#return" } } +/** + * A temporary variable generated to hold the exception thrown by a `ThrowValue` instruction. + */ class IRThrowVariable extends IRTempVariable { IRThrowVariable() { tag = ThrowTempVar() } override string getBaseString() { result = "#throw" } } +/** + * A variable generated to represent the contents of a string literal. This variable acts much like + * a read-only global variable. + */ class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral { Language::StringLiteral literal; 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 e3c78f476e9..8819826d5c3 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 @@ -94,12 +94,21 @@ module InstructionSanity { /** * Holds if instruction `instr` has multiple operands with tag `tag`. */ - query predicate duplicateOperand(Instruction instr, OperandTag tag) { - strictcount(NonPhiOperand operand | - operand = instr.getAnOperand() and - operand.getOperandTag() = tag - ) > 1 and - not tag instanceof UnmodeledUseOperandTag + query predicate duplicateOperand( + Instruction instr, string message, IRFunction func, string funcText + ) { + exists(OperandTag tag, int operandCount | + operandCount = strictcount(NonPhiOperand operand | + operand = instr.getAnOperand() and + operand.getOperandTag() = tag + ) and + operandCount > 1 and + not tag instanceof UnmodeledUseOperandTag and + message = "Instruction has " + operandCount + " operands with tag '" + tag.toString() + "'" + + " in function '$@'." and + func = instr.getEnclosingIRFunction() and + funcText = Language::getIdentityString(func.getFunction()) + ) } /** 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 6ecfd1470bb..bc67acc1be2 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 @@ -6,26 +6,6 @@ private import semmle.code.cpp.models.interfaces.Alias 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. */ @@ -137,7 +117,11 @@ private predicate operandIsPropagated(Operand operand, IntValue bitOffset) { 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. 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 a789edc7590..96b4c28db82 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 @@ -865,3 +865,17 @@ private module CachedForDebugging { result.getTag() = var.getTag() } } + +module SSASanity { + query predicate multipleOperandMemoryLocations( + OldIR::MemoryOperand operand, string message, OldIR::IRFunction func, string funcText + ) { + exists(int locationCount | + locationCount = strictcount(Alias::getOperandMemoryLocation(operand)) and + locationCount > 1 and + func = operand.getEnclosingIRFunction() and + funcText = Language::getIdentityString(func.getFunction()) and + message = "Operand has " + locationCount.toString() + " memory accesses in function '$@'." + ) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSASanity.ql b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSASanity.ql new file mode 100644 index 00000000000..1b5ee80b603 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSASanity.ql @@ -0,0 +1,8 @@ +/** + * @name Unaliased SSA Sanity Check + * @description Performs sanity checks on the SSA construction. This query should have no results. + * @kind table + * @id cpp/unaliased-ssa-sanity-check + */ + +import SSASanity diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSASanity.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSASanity.qll new file mode 100644 index 00000000000..95e8443b2a3 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSASanity.qll @@ -0,0 +1,2 @@ +private import SSAConstruction as SSA +import SSA::SSASanity diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/IntegerConstant.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/IntegerConstant.qll index 55546ba380f..6034ebc5674 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/internal/IntegerConstant.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/internal/IntegerConstant.qll @@ -192,3 +192,33 @@ predicate isGT(IntValue a, IntValue b) { hasValue(a) and hasValue(b) and a > b } */ bindingset[a, b] predicate isGE(IntValue a, IntValue b) { hasValue(a) and hasValue(b) and a >= b } + +/** + * Converts the bit count in `bits` to a byte count and a bit count in the form + * "bytes:bits". If `bits` represents an integer number of bytes, the ":bits" section is omitted. + * If `bits` does not have a known value, the result is "?". + */ +bindingset[bits] +string bitsToBytesAndBits(IntValue bits) { + exists(int bytes, int leftoverBits | + hasValue(bits) and + bytes = bits / 8 and + leftoverBits = bits % 8 and + if leftoverBits = 0 then result = bytes.toString() else result = bytes + ":" + leftoverBits + ) + or + not hasValue(bits) and result = "?" +} + +/** + * Gets a printable string for a bit offset with possibly unknown value. + */ +bindingset[bitOffset] +string getBitOffsetString(IntValue bitOffset) { + if hasValue(bitOffset) + then + if bitOffset >= 0 + then result = "+" + bitsToBytesAndBits(bitOffset) + else result = "-" + bitsToBytesAndBits(neg(bitOffset)) + else result = "+?" +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/IntegerInterval.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/IntegerInterval.qll index bc09f9c2243..cd12b9b627a 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/internal/IntegerInterval.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/internal/IntegerInterval.qll @@ -30,5 +30,5 @@ Overlap getOverlap(IntValue defStart, IntValue defEnd, IntValue useStart, IntVal bindingset[start, end] string getIntervalString(IntValue start, IntValue end) { // We represent an interval has half-open, so print it as "[start..end)". - result = "[" + intValueToString(start) + ".." + intValueToString(end) + ")" + result = "[" + bitsToBytesAndBits(start) + ".." + bitsToBytesAndBits(end) + ")" } diff --git a/cpp/ql/test/library-tests/ir/escape/points_to.expected b/cpp/ql/test/library-tests/ir/escape/points_to.expected index ac518b5dd79..6447bd81a5f 100644 --- a/cpp/ql/test/library-tests/ir/escape/points_to.expected +++ b/cpp/ql/test/library-tests/ir/escape/points_to.expected @@ -1,86 +1,86 @@ -| escape.cpp:111:18:111:21 | CopyValue | no_+0:0 | no_+0:0 | -| escape.cpp:115:19:115:28 | PointerAdd[4] | no_+0:0 | no_+0:0 | -| escape.cpp:115:20:115:23 | CopyValue | no_+0:0 | no_+0:0 | -| escape.cpp:116:19:116:28 | PointerSub[4] | no_+0:0 | no_+0:0 | -| escape.cpp:116:20:116:23 | CopyValue | no_+0:0 | no_+0:0 | -| escape.cpp:117:19:117:26 | PointerAdd[4] | no_+0:0 | no_+0:0 | -| escape.cpp:117:23:117:26 | CopyValue | no_+0:0 | no_+0:0 | -| escape.cpp:118:9:118:12 | CopyValue | no_+0:0 | no_+0:0 | -| escape.cpp:120:12:120:15 | CopyValue | no_+0:0 | no_+0:0 | -| escape.cpp:123:14:123:17 | CopyValue | no_+0:0 | no_+0:0 | -| escape.cpp:124:15:124:18 | CopyValue | no_+0:0 | no_+0:0 | -| escape.cpp:127:9:127:12 | CopyValue | no_+0:0 | no_+0:0 | -| escape.cpp:129:12:129:15 | CopyValue | no_+0:0 | no_+0:0 | -| escape.cpp:134:5:134:18 | Convert | no_Array+0:0 | no_Array+0:0 | -| escape.cpp:134:11:134:18 | Convert | no_Array+0:0 | no_Array+0:0 | -| escape.cpp:135:5:135:12 | Convert | no_Array+0:0 | no_Array+0:0 | -| escape.cpp:135:5:135:15 | PointerAdd[4] | no_Array+20:0 | no_Array+20:0 | -| escape.cpp:136:5:136:15 | PointerAdd[4] | no_Array+20:0 | no_Array+20:0 | -| escape.cpp:136:7:136:14 | Convert | no_Array+0:0 | no_Array+0:0 | -| escape.cpp:137:17:137:24 | Convert | no_Array+0:0 | no_Array+0:0 | -| escape.cpp:137:17:137:27 | PointerAdd[4] | no_Array+20:0 | no_Array+20:0 | -| escape.cpp:138:17:138:27 | PointerAdd[4] | no_Array+20:0 | no_Array+20:0 | -| escape.cpp:138:19:138:26 | Convert | no_Array+0:0 | no_Array+0:0 | -| escape.cpp:140:21:140:32 | FieldAddress[x] | no_Point+0:0 | no_Point+0:0 | -| escape.cpp:140:21:140:32 | FieldAddress[y] | no_Point+4:0 | no_Point+4:0 | -| escape.cpp:140:21:140:32 | FieldAddress[z] | no_Point+8:0 | no_Point+8:0 | -| escape.cpp:141:27:141:27 | FieldAddress[x] | no_Point+0:0 | no_Point+0:0 | -| escape.cpp:142:14:142:14 | FieldAddress[y] | no_Point+4:0 | no_Point+4:0 | -| escape.cpp:143:19:143:27 | CopyValue | no_Point+0:0 | no_Point+0:0 | -| escape.cpp:143:31:143:31 | FieldAddress[y] | no_Point+4:0 | no_Point+4:0 | -| escape.cpp:144:6:144:14 | CopyValue | no_Point+0:0 | no_Point+0:0 | -| escape.cpp:144:18:144:18 | FieldAddress[y] | no_Point+4:0 | no_Point+4:0 | -| escape.cpp:145:20:145:30 | CopyValue | no_Point+8:0 | no_Point+8:0 | -| escape.cpp:145:30:145:30 | FieldAddress[z] | no_Point+8:0 | no_Point+8:0 | -| escape.cpp:146:5:146:18 | CopyValue | no_Point+8:0 | no_Point+8:0 | -| escape.cpp:146:7:146:17 | CopyValue | no_Point+8:0 | no_Point+8:0 | -| escape.cpp:146:17:146:17 | FieldAddress[z] | no_Point+8:0 | no_Point+8:0 | -| escape.cpp:149:5:149:14 | ConvertToNonVirtualBase[Derived : Intermediate1] | no_Derived+0:0 | no_Derived+0:0 | -| escape.cpp:149:5:149:14 | ConvertToNonVirtualBase[Intermediate1 : Base] | no_Derived+0:0 | no_Derived+0:0 | -| escape.cpp:149:16:149:16 | FieldAddress[b] | no_Derived+0:0 | no_Derived+0:0 | -| escape.cpp:150:18:150:27 | ConvertToNonVirtualBase[Derived : Intermediate1] | no_Derived+0:0 | no_Derived+0:0 | -| escape.cpp:150:18:150:27 | ConvertToNonVirtualBase[Intermediate1 : Base] | no_Derived+0:0 | no_Derived+0:0 | -| escape.cpp:150:29:150:29 | FieldAddress[b] | no_Derived+0:0 | no_Derived+0:0 | -| escape.cpp:151:5:151:14 | ConvertToNonVirtualBase[Derived : Intermediate2] | no_Derived+12:0 | no_Derived+12:0 | -| escape.cpp:151:16:151:17 | FieldAddress[i2] | no_Derived+16:0 | no_Derived+16:0 | -| escape.cpp:152:19:152:28 | ConvertToNonVirtualBase[Derived : Intermediate2] | no_Derived+12:0 | no_Derived+12:0 | -| escape.cpp:152:30:152:31 | FieldAddress[i2] | no_Derived+16:0 | no_Derived+16:0 | -| escape.cpp:155:17:155:30 | CopyValue | no_ssa_addrOf+0:0 | no_ssa_addrOf+0:0 | -| escape.cpp:155:17:155:30 | Store | no_ssa_addrOf+0:0 | no_ssa_addrOf+0:0 | -| escape.cpp:158:17:158:28 | CopyValue | no_ssa_refTo+0:0 | no_ssa_refTo+0:0 | -| escape.cpp:158:17:158:28 | Store | no_ssa_refTo+0:0 | no_ssa_refTo+0:0 | -| escape.cpp:161:19:161:42 | Convert | no_ssa_refToArrayElement+0:0 | no_ssa_refToArrayElement+0:0 | -| escape.cpp:161:19:161:45 | CopyValue | no_ssa_refToArrayElement+20:0 | no_ssa_refToArrayElement+20:0 | -| escape.cpp:161:19:161:45 | PointerAdd[4] | no_ssa_refToArrayElement+20:0 | no_ssa_refToArrayElement+20:0 | -| escape.cpp:161:19:161:45 | Store | no_ssa_refToArrayElement+20:0 | no_ssa_refToArrayElement+20:0 | -| escape.cpp:164:24:164:40 | CopyValue | no_ssa_refToArray+0:0 | no_ssa_refToArray+0:0 | -| escape.cpp:164:24:164:40 | Store | no_ssa_refToArray+0:0 | no_ssa_refToArray+0:0 | -| escape.cpp:167:19:167:28 | CopyValue | passByPtr+0:0 | passByPtr+0:0 | -| escape.cpp:170:21:170:29 | CopyValue | passByRef+0:0 | passByRef+0:0 | -| escape.cpp:173:22:173:38 | CopyValue | no_ssa_passByPtr+0:0 | no_ssa_passByPtr+0:0 | -| escape.cpp:176:24:176:39 | CopyValue | no_ssa_passByRef+0:0 | no_ssa_passByRef+0:0 | -| escape.cpp:179:22:179:42 | CopyValue | no_ssa_passByPtr_ret+0:0 | no_ssa_passByPtr_ret+0:0 | -| escape.cpp:182:24:182:43 | CopyValue | no_ssa_passByRef_ret+0:0 | no_ssa_passByRef_ret+0:0 | -| escape.cpp:185:30:185:40 | CopyValue | passByPtr2+0:0 | passByPtr2+0:0 | -| escape.cpp:188:32:188:41 | CopyValue | passByRef2+0:0 | passByRef2+0:0 | -| escape.cpp:191:30:191:42 | Call | none | passByPtr3+0:0 | -| escape.cpp:191:44:191:54 | CopyValue | passByPtr3+0:0 | passByPtr3+0:0 | -| escape.cpp:194:32:194:46 | Call | none | passByRef3+0:0 | -| escape.cpp:194:32:194:59 | CopyValue | none | passByRef3+0:0 | -| escape.cpp:194:48:194:57 | CopyValue | passByRef3+0:0 | passByRef3+0:0 | -| escape.cpp:199:17:199:34 | CopyValue | no_ssa_passByPtr4+0:0 | no_ssa_passByPtr4+0:0 | -| escape.cpp:199:37:199:54 | CopyValue | no_ssa_passByPtr5+0:0 | no_ssa_passByPtr5+0:0 | -| escape.cpp:202:5:202:19 | Call | none | passByRef6+0:0 | -| escape.cpp:202:5:202:32 | CopyValue | none | passByRef6+0:0 | -| escape.cpp:202:21:202:30 | CopyValue | passByRef6+0:0 | passByRef6+0:0 | -| escape.cpp:205:5:205:19 | Call | none | no_ssa_passByRef7+0:0 | -| escape.cpp:205:5:205:39 | CopyValue | none | no_ssa_passByRef7+0:0 | -| escape.cpp:205:21:205:37 | CopyValue | no_ssa_passByRef7+0:0 | no_ssa_passByRef7+0:0 | -| escape.cpp:209:14:209:25 | Call | none | no_ssa_c+0:0 | -| escape.cpp:217:14:217:16 | CopyValue | c2+0:0 | c2+0:0 | -| escape.cpp:221:8:221:19 | Call | none | c3+0:0 | -| escape.cpp:225:17:225:28 | Call | none | c4+0:0 | -| escape.cpp:247:2:247:27 | Store | condEscape1+0:0 | condEscape1+0:0 | -| escape.cpp:247:16:247:27 | CopyValue | condEscape1+0:0 | condEscape1+0:0 | -| escape.cpp:249:9:249:34 | Store | condEscape2+0:0 | condEscape2+0:0 | -| escape.cpp:249:23:249:34 | CopyValue | condEscape2+0:0 | condEscape2+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 index 04dbf0e0060..7c265974b10 100644 --- a/cpp/ql/test/library-tests/ir/escape/points_to.ql +++ b/cpp/ql/test/library-tests/ir/escape/points_to.ql @@ -4,6 +4,7 @@ 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 @@ -12,21 +13,21 @@ where ( exists(Variable var, int rawBitOffset, int unBitOffset | RawAA::resultPointsTo(rawInstr, Raw::getIRUserVariable(_, var), rawBitOffset) and - rawPointsTo = var.toString() + RawAA::getBitOffsetString(rawBitOffset) and + rawPointsTo = var.toString() + getBitOffsetString(rawBitOffset) and UnAA::resultPointsTo(unInstr, Un::getIRUserVariable(_, var), unBitOffset) and - unPointsTo = var.toString() + UnAA::getBitOffsetString(unBitOffset) + 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() + UnAA::getBitOffsetString(unBitOffset) + unPointsTo = var.toString() + getBitOffsetString(unBitOffset) ) or exists(Variable var, int rawBitOffset | RawAA::resultPointsTo(rawInstr, Raw::getIRUserVariable(_, var), rawBitOffset) and - rawPointsTo = var.toString() + RawAA::getBitOffsetString(rawBitOffset) and + rawPointsTo = var.toString() + getBitOffsetString(rawBitOffset) and not UnAA::resultPointsTo(unInstr, Un::getIRUserVariable(_, var), _) and unPointsTo = "none" ) diff --git a/cpp/ql/test/library-tests/ir/ir/aliased_ssa_ssa_sanity.expected b/cpp/ql/test/library-tests/ir/ir/aliased_ssa_ssa_sanity.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/cpp/ql/test/library-tests/ir/ir/aliased_ssa_ssa_sanity.qlref b/cpp/ql/test/library-tests/ir/ir/aliased_ssa_ssa_sanity.qlref new file mode 100644 index 00000000000..8e348011785 --- /dev/null +++ b/cpp/ql/test/library-tests/ir/ir/aliased_ssa_ssa_sanity.qlref @@ -0,0 +1 @@ +semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSASanity.ql \ No newline at end of file diff --git a/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_ssa_sanity.expected b/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_ssa_sanity.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_ssa_sanity.qlref b/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_ssa_sanity.qlref new file mode 100644 index 00000000000..18bf9212dbf --- /dev/null +++ b/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_ssa_sanity.qlref @@ -0,0 +1 @@ +semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSASanity.ql \ No newline at end of file diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ssa_sanity.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ssa_sanity.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ssa_sanity.qlref b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ssa_sanity.qlref new file mode 100644 index 00000000000..8e348011785 --- /dev/null +++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ssa_sanity.qlref @@ -0,0 +1 @@ +semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSASanity.ql \ No newline at end of file diff --git a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ssa_sanity.expected b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ssa_sanity.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ssa_sanity.qlref b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ssa_sanity.qlref new file mode 100644 index 00000000000..18bf9212dbf --- /dev/null +++ b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ssa_sanity.qlref @@ -0,0 +1 @@ +semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSASanity.ql \ No newline at end of file diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRVariable.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRVariable.qll index e10e266b2ab..f9efad42fb0 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRVariable.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRVariable.qll @@ -13,14 +13,20 @@ IRUserVariable getIRUserVariable(Language::Function func, Language::Variable var } /** - * Represents a variable referenced by the IR for a function. The variable may - * be a user-declared variable (`IRUserVariable`) or a temporary variable - * generated by the AST-to-IR translation (`IRTempVariable`). + * A variable referenced by the IR for a function. The variable may be a user-declared variable + * (`IRUserVariable`) or a temporary variable generated by the AST-to-IR translation + * (`IRTempVariable`). */ -abstract class IRVariable extends TIRVariable { +class IRVariable extends TIRVariable { Language::Function func; - abstract string toString(); + IRVariable() { + this = TIRUserVariable(_, _, func) or + this = TIRTempVariable(func, _, _, _) or + this = TIRStringLiteral(func, _, _, _) + } + + string toString() { none() } /** * Holds if this variable's value cannot be changed within a function. Currently used for string @@ -41,19 +47,19 @@ abstract class IRVariable extends TIRVariable { /** * Gets the type of the variable. */ - abstract Language::LanguageType getLanguageType(); + Language::LanguageType getLanguageType() { none() } /** * Gets the AST node that declared this variable, or that introduced this * variable as part of the AST-to-IR translation. */ - abstract Language::AST getAST(); + Language::AST getAST() { none() } /** * Gets an identifier string for the variable. This identifier is unique * within the function. */ - abstract string getUniqueId(); + string getUniqueId() { none() } /** * Gets the source location of this variable. @@ -72,7 +78,7 @@ abstract class IRVariable extends TIRVariable { } /** - * Represents a user-declared variable referenced by the IR for a function. + * A user-declared variable referenced by the IR for a function. */ class IRUserVariable extends IRVariable, TIRUserVariable { Language::Variable var; @@ -97,20 +103,34 @@ class IRUserVariable extends IRVariable, TIRUserVariable { } /** - * Represents a variable (user-declared or temporary) that is allocated on the - * stack. This includes all parameters, non-static local variables, and - * temporary variables. + * A variable (user-declared or temporary) that is allocated on the stack. This includes all + * parameters, non-static local variables, and temporary variables. */ -abstract class IRAutomaticVariable extends IRVariable { } +class IRAutomaticVariable extends IRVariable { + IRAutomaticVariable() { + exists(Language::Variable var | + this = TIRUserVariable(var, _, func) and + Language::isVariableAutomatic(var) + ) + or + this = TIRTempVariable(func, _, _, _) + } +} +/** + * A user-declared variable that is allocated on the stack. This includes all parameters and + * non-static local variables. + */ class IRAutomaticUserVariable extends IRUserVariable, IRAutomaticVariable { override Language::AutomaticVariable var; - IRAutomaticUserVariable() { Language::isVariableAutomatic(var) } - final override Language::AutomaticVariable getVariable() { result = var } } +/** + * A user-declared variable that is not allocated on the stack. This includes all global variables, + * namespace-scope variables, static fields, and static local variables. + */ class IRStaticUserVariable extends IRUserVariable { override Language::StaticVariable var; @@ -119,10 +139,19 @@ class IRStaticUserVariable extends IRUserVariable { final override Language::StaticVariable getVariable() { result = var } } -abstract class IRGeneratedVariable extends IRVariable { +/** + * A variable that is not user-declared. This includes temporary variables generated as part of IR + * construction, as well as string literals. + */ +class IRGeneratedVariable extends IRVariable { Language::AST ast; Language::LanguageType type; + IRGeneratedVariable() { + this = TIRTempVariable(func, ast, _, type) or + this = TIRStringLiteral(func, ast, type, _) + } + final override Language::LanguageType getLanguageType() { result = type } final override Language::AST getAST() { result = ast } @@ -144,6 +173,11 @@ IRTempVariable getIRTempVariable(Language::AST ast, TempVariableTag tag) { result.getTag() = tag } +/** + * A temporary variable introduced by IR construction. The most common examples are the variable + * generated to hold the return value of afunction, or the variable generated to hold the result of + * a condition operator (`a ? b : c`). + */ class IRTempVariable extends IRGeneratedVariable, IRAutomaticVariable, TIRTempVariable { TempVariableTag tag; @@ -158,18 +192,28 @@ class IRTempVariable extends IRGeneratedVariable, IRAutomaticVariable, TIRTempVa override string getBaseString() { result = "#temp" } } +/** + * A temporary variable generated to hold the return value of a function. + */ class IRReturnVariable extends IRTempVariable { IRReturnVariable() { tag = ReturnValueTempVar() } final override string toString() { result = "#return" } } +/** + * A temporary variable generated to hold the exception thrown by a `ThrowValue` instruction. + */ class IRThrowVariable extends IRTempVariable { IRThrowVariable() { tag = ThrowTempVar() } override string getBaseString() { result = "#throw" } } +/** + * A variable generated to represent the contents of a string literal. This variable acts much like + * a read-only global variable. + */ class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral { Language::StringLiteral literal; 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 e3c78f476e9..8819826d5c3 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 @@ -94,12 +94,21 @@ module InstructionSanity { /** * Holds if instruction `instr` has multiple operands with tag `tag`. */ - query predicate duplicateOperand(Instruction instr, OperandTag tag) { - strictcount(NonPhiOperand operand | - operand = instr.getAnOperand() and - operand.getOperandTag() = tag - ) > 1 and - not tag instanceof UnmodeledUseOperandTag + query predicate duplicateOperand( + Instruction instr, string message, IRFunction func, string funcText + ) { + exists(OperandTag tag, int operandCount | + operandCount = strictcount(NonPhiOperand operand | + operand = instr.getAnOperand() and + operand.getOperandTag() = tag + ) and + operandCount > 1 and + not tag instanceof UnmodeledUseOperandTag and + message = "Instruction has " + operandCount + " operands with tag '" + tag.toString() + "'" + + " in function '$@'." and + func = instr.getEnclosingIRFunction() and + funcText = Language::getIdentityString(func.getFunction()) + ) } /** diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRVariable.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRVariable.qll index e10e266b2ab..f9efad42fb0 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRVariable.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRVariable.qll @@ -13,14 +13,20 @@ IRUserVariable getIRUserVariable(Language::Function func, Language::Variable var } /** - * Represents a variable referenced by the IR for a function. The variable may - * be a user-declared variable (`IRUserVariable`) or a temporary variable - * generated by the AST-to-IR translation (`IRTempVariable`). + * A variable referenced by the IR for a function. The variable may be a user-declared variable + * (`IRUserVariable`) or a temporary variable generated by the AST-to-IR translation + * (`IRTempVariable`). */ -abstract class IRVariable extends TIRVariable { +class IRVariable extends TIRVariable { Language::Function func; - abstract string toString(); + IRVariable() { + this = TIRUserVariable(_, _, func) or + this = TIRTempVariable(func, _, _, _) or + this = TIRStringLiteral(func, _, _, _) + } + + string toString() { none() } /** * Holds if this variable's value cannot be changed within a function. Currently used for string @@ -41,19 +47,19 @@ abstract class IRVariable extends TIRVariable { /** * Gets the type of the variable. */ - abstract Language::LanguageType getLanguageType(); + Language::LanguageType getLanguageType() { none() } /** * Gets the AST node that declared this variable, or that introduced this * variable as part of the AST-to-IR translation. */ - abstract Language::AST getAST(); + Language::AST getAST() { none() } /** * Gets an identifier string for the variable. This identifier is unique * within the function. */ - abstract string getUniqueId(); + string getUniqueId() { none() } /** * Gets the source location of this variable. @@ -72,7 +78,7 @@ abstract class IRVariable extends TIRVariable { } /** - * Represents a user-declared variable referenced by the IR for a function. + * A user-declared variable referenced by the IR for a function. */ class IRUserVariable extends IRVariable, TIRUserVariable { Language::Variable var; @@ -97,20 +103,34 @@ class IRUserVariable extends IRVariable, TIRUserVariable { } /** - * Represents a variable (user-declared or temporary) that is allocated on the - * stack. This includes all parameters, non-static local variables, and - * temporary variables. + * A variable (user-declared or temporary) that is allocated on the stack. This includes all + * parameters, non-static local variables, and temporary variables. */ -abstract class IRAutomaticVariable extends IRVariable { } +class IRAutomaticVariable extends IRVariable { + IRAutomaticVariable() { + exists(Language::Variable var | + this = TIRUserVariable(var, _, func) and + Language::isVariableAutomatic(var) + ) + or + this = TIRTempVariable(func, _, _, _) + } +} +/** + * A user-declared variable that is allocated on the stack. This includes all parameters and + * non-static local variables. + */ class IRAutomaticUserVariable extends IRUserVariable, IRAutomaticVariable { override Language::AutomaticVariable var; - IRAutomaticUserVariable() { Language::isVariableAutomatic(var) } - final override Language::AutomaticVariable getVariable() { result = var } } +/** + * A user-declared variable that is not allocated on the stack. This includes all global variables, + * namespace-scope variables, static fields, and static local variables. + */ class IRStaticUserVariable extends IRUserVariable { override Language::StaticVariable var; @@ -119,10 +139,19 @@ class IRStaticUserVariable extends IRUserVariable { final override Language::StaticVariable getVariable() { result = var } } -abstract class IRGeneratedVariable extends IRVariable { +/** + * A variable that is not user-declared. This includes temporary variables generated as part of IR + * construction, as well as string literals. + */ +class IRGeneratedVariable extends IRVariable { Language::AST ast; Language::LanguageType type; + IRGeneratedVariable() { + this = TIRTempVariable(func, ast, _, type) or + this = TIRStringLiteral(func, ast, type, _) + } + final override Language::LanguageType getLanguageType() { result = type } final override Language::AST getAST() { result = ast } @@ -144,6 +173,11 @@ IRTempVariable getIRTempVariable(Language::AST ast, TempVariableTag tag) { result.getTag() = tag } +/** + * A temporary variable introduced by IR construction. The most common examples are the variable + * generated to hold the return value of afunction, or the variable generated to hold the result of + * a condition operator (`a ? b : c`). + */ class IRTempVariable extends IRGeneratedVariable, IRAutomaticVariable, TIRTempVariable { TempVariableTag tag; @@ -158,18 +192,28 @@ class IRTempVariable extends IRGeneratedVariable, IRAutomaticVariable, TIRTempVa override string getBaseString() { result = "#temp" } } +/** + * A temporary variable generated to hold the return value of a function. + */ class IRReturnVariable extends IRTempVariable { IRReturnVariable() { tag = ReturnValueTempVar() } final override string toString() { result = "#return" } } +/** + * A temporary variable generated to hold the exception thrown by a `ThrowValue` instruction. + */ class IRThrowVariable extends IRTempVariable { IRThrowVariable() { tag = ThrowTempVar() } override string getBaseString() { result = "#throw" } } +/** + * A variable generated to represent the contents of a string literal. This variable acts much like + * a read-only global variable. + */ class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral { Language::StringLiteral literal; 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 e3c78f476e9..8819826d5c3 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 @@ -94,12 +94,21 @@ module InstructionSanity { /** * Holds if instruction `instr` has multiple operands with tag `tag`. */ - query predicate duplicateOperand(Instruction instr, OperandTag tag) { - strictcount(NonPhiOperand operand | - operand = instr.getAnOperand() and - operand.getOperandTag() = tag - ) > 1 and - not tag instanceof UnmodeledUseOperandTag + query predicate duplicateOperand( + Instruction instr, string message, IRFunction func, string funcText + ) { + exists(OperandTag tag, int operandCount | + operandCount = strictcount(NonPhiOperand operand | + operand = instr.getAnOperand() and + operand.getOperandTag() = tag + ) and + operandCount > 1 and + not tag instanceof UnmodeledUseOperandTag and + message = "Instruction has " + operandCount + " operands with tag '" + tag.toString() + "'" + + " in function '$@'." and + func = instr.getEnclosingIRFunction() and + funcText = Language::getIdentityString(func.getFunction()) + ) } /** 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 a789edc7590..96b4c28db82 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 @@ -865,3 +865,17 @@ private module CachedForDebugging { result.getTag() = var.getTag() } } + +module SSASanity { + query predicate multipleOperandMemoryLocations( + OldIR::MemoryOperand operand, string message, OldIR::IRFunction func, string funcText + ) { + exists(int locationCount | + locationCount = strictcount(Alias::getOperandMemoryLocation(operand)) and + locationCount > 1 and + func = operand.getEnclosingIRFunction() and + funcText = Language::getIdentityString(func.getFunction()) and + message = "Operand has " + locationCount.toString() + " memory accesses in function '$@'." + ) + } +} diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSASanity.ql b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSASanity.ql new file mode 100644 index 00000000000..43f303dd024 --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSASanity.ql @@ -0,0 +1,8 @@ +/** + * @name Unaliased SSA Sanity Check + * @description Performs sanity checks on the SSA construction. This query should have no results. + * @kind table + * @id csharp/unaliased-ssa-sanity-check + */ + +import SSASanity diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSASanity.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSASanity.qll new file mode 100644 index 00000000000..95e8443b2a3 --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSASanity.qll @@ -0,0 +1,2 @@ +private import SSAConstruction as SSA +import SSA::SSASanity diff --git a/csharp/ql/src/semmle/code/csharp/ir/internal/IntegerConstant.qll b/csharp/ql/src/semmle/code/csharp/ir/internal/IntegerConstant.qll index 55546ba380f..6034ebc5674 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/internal/IntegerConstant.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/internal/IntegerConstant.qll @@ -192,3 +192,33 @@ predicate isGT(IntValue a, IntValue b) { hasValue(a) and hasValue(b) and a > b } */ bindingset[a, b] predicate isGE(IntValue a, IntValue b) { hasValue(a) and hasValue(b) and a >= b } + +/** + * Converts the bit count in `bits` to a byte count and a bit count in the form + * "bytes:bits". If `bits` represents an integer number of bytes, the ":bits" section is omitted. + * If `bits` does not have a known value, the result is "?". + */ +bindingset[bits] +string bitsToBytesAndBits(IntValue bits) { + exists(int bytes, int leftoverBits | + hasValue(bits) and + bytes = bits / 8 and + leftoverBits = bits % 8 and + if leftoverBits = 0 then result = bytes.toString() else result = bytes + ":" + leftoverBits + ) + or + not hasValue(bits) and result = "?" +} + +/** + * Gets a printable string for a bit offset with possibly unknown value. + */ +bindingset[bitOffset] +string getBitOffsetString(IntValue bitOffset) { + if hasValue(bitOffset) + then + if bitOffset >= 0 + then result = "+" + bitsToBytesAndBits(bitOffset) + else result = "-" + bitsToBytesAndBits(neg(bitOffset)) + else result = "+?" +} diff --git a/csharp/ql/src/semmle/code/csharp/ir/internal/IntegerInterval.qll b/csharp/ql/src/semmle/code/csharp/ir/internal/IntegerInterval.qll index bc09f9c2243..cd12b9b627a 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/internal/IntegerInterval.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/internal/IntegerInterval.qll @@ -30,5 +30,5 @@ Overlap getOverlap(IntValue defStart, IntValue defEnd, IntValue useStart, IntVal bindingset[start, end] string getIntervalString(IntValue start, IntValue end) { // We represent an interval has half-open, so print it as "[start..end)". - result = "[" + intValueToString(start) + ".." + intValueToString(end) + ")" + result = "[" + bitsToBytesAndBits(start) + ".." + bitsToBytesAndBits(end) + ")" } diff --git a/csharp/ql/test/library-tests/ir/ir/unaliased_ssa_ssa_sanity.expected b/csharp/ql/test/library-tests/ir/ir/unaliased_ssa_ssa_sanity.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/csharp/ql/test/library-tests/ir/ir/unaliased_ssa_ssa_sanity.qlref b/csharp/ql/test/library-tests/ir/ir/unaliased_ssa_ssa_sanity.qlref new file mode 100644 index 00000000000..1b7d4a7996a --- /dev/null +++ b/csharp/ql/test/library-tests/ir/ir/unaliased_ssa_ssa_sanity.qlref @@ -0,0 +1 @@ +semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSASanity.ql \ No newline at end of file