diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/InstructionTag.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/InstructionTag.qll index 617fa9ffd89..cb4f75c1501 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/InstructionTag.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/InstructionTag.qll @@ -72,7 +72,19 @@ newtype TInstructionTag = AsmInputTag(int elementIndex) { exists(AsmStmt asm | exists(asm.getChild(elementIndex))) } or ThisAddressTag() or ThisLoadTag() or - StructuredBindingAccessTag() + StructuredBindingAccessTag() or + // The next three cases handle generation of the constants -1, 0 and 1 for __except handling. + TryExceptGenerateNegativeOne() or + TryExceptGenerateZero() or + TryExceptGenerateOne() or + // The next three cases handle generation of comparisons for __except handling. + TryExceptCompareNegativeOne() or + TryExceptCompareZero() or + TryExceptCompareOne() or + // The next three cases handle generation of branching for __except handling. + TryExceptCompareNegativeOneBranch() or + TryExceptCompareZeroBranch() or + TryExceptCompareOneBranch() class InstructionTag extends TInstructionTag { final string toString() { result = "Tag" } @@ -224,4 +236,22 @@ string getInstructionTagId(TInstructionTag tag) { tag = ThisLoadTag() and result = "ThisLoad" or tag = StructuredBindingAccessTag() and result = "StructuredBindingAccess" + or + tag = TryExceptCompareNegativeOne() and result = "TryExceptCompareNegativeOne" + or + tag = TryExceptCompareZero() and result = "TryExceptCompareZero" + or + tag = TryExceptCompareOne() and result = "TryExceptCompareOne" + or + tag = TryExceptGenerateNegativeOne() and result = "TryExceptGenerateNegativeOne" + or + tag = TryExceptGenerateZero() and result = "TryExceptGenerateNegativeOne" + or + tag = TryExceptGenerateOne() and result = "TryExceptGenerateOne" + or + tag = TryExceptCompareNegativeOneBranch() and result = "TryExceptCompareNegativeOneBranch" + or + tag = TryExceptCompareZeroBranch() and result = "TryExceptCompareZeroBranch" + or + tag = TryExceptCompareOneBranch() and result = "TryExceptCompareOneBranch" } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll index 3a350ea0e1a..81c04c736b4 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll @@ -675,6 +675,7 @@ newtype TTranslatedElement = } or // A statement TTranslatedStmt(Stmt stmt) { translateStmt(stmt) } or + TTranslatedMicrosoftTryExceptHandler(MicrosoftTryExceptStmt stmt) or // A function TTranslatedFunction(Function func) { translateFunction(func) } or // A constructor init list diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedStmt.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedStmt.qll index 10294258515..39922bbec7f 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedStmt.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedStmt.qll @@ -13,6 +13,222 @@ private import TranslatedInitialization TranslatedStmt getTranslatedStmt(Stmt stmt) { result.getAst() = stmt } +TranslatedMicrosoftTryExceptHandler getTranslatedMicrosoftTryExceptHandler( + MicrosoftTryExceptStmt tryExcept +) { + result.getAst() = tryExcept.getExcept() +} + +class TranslatedMicrosoftTryExceptHandler extends TranslatedElement, + TTranslatedMicrosoftTryExceptHandler { + MicrosoftTryExceptStmt tryExcept; + + TranslatedMicrosoftTryExceptHandler() { this = TTranslatedMicrosoftTryExceptHandler(tryExcept) } + + final override string toString() { result = tryExcept.toString() } + + final override Locatable getAst() { result = tryExcept.getExcept() } + + override Instruction getFirstInstruction() { result = getChild(0).getFirstInstruction() } + + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { + // t1 = -1 + tag = TryExceptGenerateNegativeOne() and + opcode instanceof Opcode::Constant and + resultType = getIntType() + or + // t2 = cmp t1, condition + tag = TryExceptCompareNegativeOne() and + opcode instanceof Opcode::CompareEQ and + resultType = getBoolType() + or + // if t2 goto ... else goto ... + tag = TryExceptCompareNegativeOneBranch() and + opcode instanceof Opcode::ConditionalBranch and + resultType = getVoidType() + or + // t1 = 0 + tag = TryExceptGenerateZero() and + opcode instanceof Opcode::Constant and + resultType = getIntType() + or + // t2 = cmp t1, condition + tag = TryExceptCompareZero() and + opcode instanceof Opcode::CompareEQ and + resultType = getBoolType() + or + // if t2 goto ... else goto ... + tag = TryExceptCompareZeroBranch() and + opcode instanceof Opcode::ConditionalBranch and + resultType = getVoidType() + or + // t1 = 1 + tag = TryExceptGenerateOne() and + opcode instanceof Opcode::Constant and + resultType = getIntType() + or + // t2 = cmp t1, condition + tag = TryExceptCompareOne() and + opcode instanceof Opcode::CompareEQ and + resultType = getBoolType() + or + // if t2 goto ... else goto ... + tag = TryExceptCompareOneBranch() and + opcode instanceof Opcode::ConditionalBranch and + resultType = getVoidType() + or + // unwind stack + tag = UnwindTag() and + opcode instanceof Opcode::Unwind and + resultType = getVoidType() + } + + final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) { + tag = TryExceptCompareNegativeOne() and + ( + operandTag instanceof LeftOperandTag and + result = getTranslatedCondition().getResult() + or + operandTag instanceof RightOperandTag and + result = getInstruction(TryExceptGenerateNegativeOne()) + ) + or + tag = TryExceptCompareNegativeOneBranch() and + operandTag instanceof ConditionOperandTag and + result = getInstruction(TryExceptCompareNegativeOne()) + or + tag = TryExceptCompareZero() and + ( + operandTag instanceof LeftOperandTag and + result = getTranslatedCondition().getResult() + or + operandTag instanceof RightOperandTag and + result = getInstruction(TryExceptGenerateZero()) + ) + or + tag = TryExceptCompareZeroBranch() and + operandTag instanceof ConditionOperandTag and + result = getInstruction(TryExceptCompareZero()) + or + tag = TryExceptCompareOne() and + ( + operandTag instanceof LeftOperandTag and + result = getTranslatedCondition().getResult() + or + operandTag instanceof RightOperandTag and + result = getInstruction(TryExceptGenerateOne()) + ) + or + tag = TryExceptCompareOneBranch() and + operandTag instanceof ConditionOperandTag and + result = getInstruction(TryExceptCompareOne()) + } + + override string getInstructionConstantValue(InstructionTag tag) { + tag = TryExceptGenerateNegativeOne() and + result = "-1" + or + tag = TryExceptGenerateZero() and + result = "0" + or + tag = TryExceptGenerateOne() and + result = "1" + } + + override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { + // Generate -1 -> Compare condition + tag = TryExceptGenerateNegativeOne() and + kind instanceof GotoEdge and + result = getInstruction(TryExceptCompareNegativeOne()) + or + // Compare condition -> Branch + tag = TryExceptCompareNegativeOne() and + kind instanceof GotoEdge and + result = getInstruction(TryExceptCompareNegativeOneBranch()) + or + // Branch -> Unwind or Generate 0 + tag = TryExceptCompareNegativeOneBranch() and + ( + kind instanceof TrueEdge and + // TODO: This is not really correct. The semantics of `EXCEPTION_CONTINUE_EXECUTION` is that + // we should continue execution at the point where the exception occurred. But we don't have + // any instruction to model this behavior. + result = getInstruction(UnwindTag()) + or + kind instanceof FalseEdge and + result = getInstruction(TryExceptGenerateZero()) + ) + or + // Generate 0 -> Compare condition + tag = TryExceptGenerateZero() and + kind instanceof GotoEdge and + result = getInstruction(TryExceptCompareZero()) + or + // Compare condition -> Branch + tag = TryExceptCompareZero() and + kind instanceof GotoEdge and + result = getInstruction(TryExceptCompareZeroBranch()) + or + // Branch -> Unwind or Generate 1 + tag = TryExceptCompareZeroBranch() and + ( + kind instanceof TrueEdge and + result = getInstruction(UnwindTag()) + or + kind instanceof FalseEdge and + result = getInstruction(TryExceptGenerateOne()) + ) + or + // Generate 1 -> Compare condition + tag = TryExceptGenerateOne() and + kind instanceof GotoEdge and + result = getInstruction(TryExceptCompareOne()) + or + // Compare condition -> Branch + tag = TryExceptCompareOne() and + kind instanceof GotoEdge and + result = getInstruction(TryExceptCompareOneBranch()) + or + // Branch -> Handler (the condition value is always 0, -1 or 1, and we've checked for 0 or -1 already.) + tag = TryExceptCompareOneBranch() and + ( + kind instanceof TrueEdge and + result = getTranslatedHandler().getFirstInstruction() + ) + or + // Unwind -> Parent + tag = UnwindTag() and + kind instanceof GotoEdge and + result = getParent().getChildSuccessor(this) + } + + override Instruction getChildSuccessor(TranslatedElement child) { + child = getTranslatedCondition() and + result = getInstruction(TryExceptGenerateNegativeOne()) + or + child = getTranslatedHandler() and + result = getParent().getChildSuccessor(this) + } + + private TranslatedExpr getTranslatedCondition() { + result = getTranslatedExpr(tryExcept.getCondition()) + } + + private TranslatedStmt getTranslatedHandler() { + result = getTranslatedStmt(tryExcept.getExcept()) + } + + override TranslatedElement getChild(int id) { + id = 0 and + result = getTranslatedCondition() + or + id = 1 and + result = getTranslatedHandler() + } + + final override Function getFunction() { result = tryExcept.getEnclosingFunction() } +} + abstract class TranslatedStmt extends TranslatedElement, TTranslatedStmt { Stmt stmt; @@ -275,12 +491,12 @@ private class TryOrMicrosoftTryStmt extends Stmt { result = this.(MicrosoftTryStmt).getStmt() } - /** Gets the `i`th `catch block` statement of this statement. */ - Stmt getHandlerStmt(int i) { - result = this.(TryStmt).getChild(i + 1) + /** Gets the `i`th translated handler of this statement. */ + TranslatedElement getTranslatedHandler(int index) { + result = getTranslatedStmt(this.(TryStmt).getChild(index + 1)) or - i = 0 and - result = this.(MicrosoftTryExceptStmt).getExcept() + index = 0 and + result = getTranslatedMicrosoftTryExceptHandler(this) } /** Gets the `finally` statement (usually a BlockStmt), if any. */ @@ -344,9 +560,7 @@ class TranslatedTryStmt extends TranslatedStmt { result = getHandler(0).getFirstInstruction() } - private TranslatedStmt getHandler(int index) { - result = getTranslatedStmt(stmt.getHandlerStmt(index)) - } + private TranslatedElement getHandler(int index) { result = stmt.getTranslatedHandler(index) } private TranslatedStmt getFinally() { result = getTranslatedStmt(stmt.getFinally()) } diff --git a/cpp/ql/test/library-tests/ir/ir/raw_consistency.expected b/cpp/ql/test/library-tests/ir/ir/raw_consistency.expected index 59b53b8b61d..ddd8969343c 100644 --- a/cpp/ql/test/library-tests/ir/ir/raw_consistency.expected +++ b/cpp/ql/test/library-tests/ir/ir/raw_consistency.expected @@ -6,7 +6,6 @@ missingOperandType duplicateChiOperand sideEffectWithoutPrimary instructionWithoutSuccessor -| try_except.c:13:13:13:13 | Constant: 0 | Instruction 'Constant: 0' has no successors in function '$@'. | try_except.c:6:6:6:6 | void f() | void f() | ambiguousSuccessors unexplainedLoop unnecessaryPhiInstruction @@ -20,6 +19,8 @@ useNotDominatedByDefinition | ir.cpp:1486:8:1486:8 | Unary | Operand 'Unary' is not dominated by its definition in function '$@'. | ir.cpp:1486:8:1486:8 | void StructuredBindingDataMemberStruct::StructuredBindingDataMemberStruct() | void StructuredBindingDataMemberStruct::StructuredBindingDataMemberStruct() | | ir.cpp:1751:51:1751:51 | Address | Operand 'Address' is not dominated by its definition in function '$@'. | ir.cpp:1750:5:1750:34 | int implicit_copy_constructor_test(CopyConstructorTestNonVirtualClass const&, CopyConstructorTestVirtualClass const&) | int implicit_copy_constructor_test(CopyConstructorTestNonVirtualClass const&, CopyConstructorTestVirtualClass const&) | | ir.cpp:1752:48:1752:48 | Address | Operand 'Address' is not dominated by its definition in function '$@'. | ir.cpp:1750:5:1750:34 | int implicit_copy_constructor_test(CopyConstructorTestNonVirtualClass const&, CopyConstructorTestVirtualClass const&) | int implicit_copy_constructor_test(CopyConstructorTestNonVirtualClass const&, CopyConstructorTestVirtualClass const&) | +| try_except.c:13:13:13:13 | Left | Operand 'Left' is not dominated by its definition in function '$@'. | try_except.c:6:6:6:6 | void f() | void f() | +| try_except.c:13:13:13:13 | Left | Operand 'Left' is not dominated by its definition in function '$@'. | try_except.c:6:6:6:6 | void f() | void f() | switchInstructionWithoutDefaultEdge notMarkedAsConflated wronglyMarkedAsConflated diff --git a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected index 155f1e6dccc..71c6b65d1d5 100644 --- a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected +++ b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected @@ -10434,20 +10434,42 @@ try_except.c: # 11| r11_2(int) = Constant[0] : # 11| v11_3(void) = Call[ProbeFunction] : func:r11_1, 0:r11_2 # 11| mu11_4(unknown) = ^CallSideEffect : ~m? -#-----| Goto -> Block 3 +#-----| Goto -> Block 6 # 13| Block 1 -# 13| r13_1(int) = Constant[0] : +# 13| r13_1(int) = Constant[0] : +# 13| r13_2(bool) = CompareEQ : r13_8, r13_1 +# 13| v13_3(void) = ConditionalBranch : r13_2 +#-----| False -> Block 2 +#-----| True -> Block 3 -# 14| Block 2 +# 13| Block 2 +# 13| r13_4(int) = Constant[1] : +# 13| r13_5(bool) = CompareEQ : r13_8, r13_4 +# 13| v13_6(void) = ConditionalBranch : r13_5 +#-----| True -> Block 5 + +# 13| Block 3 +# 13| v13_7(void) = Unwind : +#-----| Goto -> Block 6 + +# 13| Block 4 +# 13| r13_8(int) = Constant[0] : +# 13| r13_9(int) = Constant[-1] : +# 13| r13_10(bool) = CompareEQ : r13_8, r13_9 +# 13| v13_11(void) = ConditionalBranch : r13_10 +#-----| False -> Block 1 +#-----| True -> Block 3 + +# 14| Block 5 # 14| r14_1(glval) = FunctionAddress[sink] : # 14| r14_2(glval) = VariableAddress[x] : # 14| r14_3(int) = Load[x] : &:r14_2, ~m? # 14| v14_4(void) = Call[sink] : func:r14_1, 0:r14_3 # 14| mu14_5(unknown) = ^CallSideEffect : ~m? -#-----| Goto -> Block 3 +#-----| Goto -> Block 6 -# 16| Block 3 +# 16| Block 6 # 16| v16_1(void) = NoOp : # 6| v6_4(void) = ReturnVoid : # 6| v6_5(void) = AliasedUse : ~m? diff --git a/cpp/ql/test/library-tests/syntax-zoo/raw_consistency.expected b/cpp/ql/test/library-tests/syntax-zoo/raw_consistency.expected index ef896f91bdf..d68bb1cfde7 100644 --- a/cpp/ql/test/library-tests/syntax-zoo/raw_consistency.expected +++ b/cpp/ql/test/library-tests/syntax-zoo/raw_consistency.expected @@ -31,9 +31,6 @@ instructionWithoutSuccessor | misc.c:174:17:174:22 | CallSideEffect: call to getInt | Instruction 'CallSideEffect: call to getInt' has no successors in function '$@'. | misc.c:168:6:168:8 | void vla() | void vla() | | misc.c:174:30:174:35 | CallSideEffect: call to getInt | Instruction 'CallSideEffect: call to getInt' has no successors in function '$@'. | misc.c:168:6:168:8 | void vla() | void vla() | | misc.c:174:55:174:60 | Store: (char ****)... | Instruction 'Store: (char ****)...' has no successors in function '$@'. | misc.c:168:6:168:8 | void vla() | void vla() | -| ms_try_except.cpp:9:19:9:19 | Load: j | Instruction 'Load: j' has no successors in function '$@'. | ms_try_except.cpp:2:6:2:18 | void ms_try_except(int) | void ms_try_except(int) | -| ms_try_except.cpp:19:17:19:21 | Sub: ... - ... | Instruction 'Sub: ... - ...' has no successors in function '$@'. | ms_try_except.cpp:2:6:2:18 | void ms_try_except(int) | void ms_try_except(int) | -| ms_try_mix.cpp:20:15:20:39 | Constant: 1 | Instruction 'Constant: 1' has no successors in function '$@'. | ms_try_mix.cpp:10:6:10:18 | void ms_except_mix(int) | void ms_except_mix(int) | | ms_try_mix.cpp:33:13:33:19 | ThrowValue: throw ... | Instruction 'ThrowValue: throw ...' has no successors in function '$@'. | ms_try_mix.cpp:27:6:27:19 | void ms_finally_mix(int) | void ms_finally_mix(int) | | ms_try_mix.cpp:51:5:51:11 | ThrowValue: throw ... | Instruction 'ThrowValue: throw ...' has no successors in function '$@'. | ms_try_mix.cpp:47:6:47:28 | void ms_empty_finally_at_end() | void ms_empty_finally_at_end() | | stmt_expr.cpp:27:5:27:15 | Store: ... = ... | Instruction 'Store: ... = ...' has no successors in function '$@'. | stmt_expr.cpp:21:6:21:6 | void stmtexpr::g(int) | void stmtexpr::g(int) | @@ -120,6 +117,10 @@ backEdgeCountMismatch useNotDominatedByDefinition | VacuousDestructorCall.cpp:2:29:2:29 | Address | Operand 'Address' is not dominated by its definition in function '$@'. | VacuousDestructorCall.cpp:2:6:2:6 | void CallDestructor(int, int*) | void CallDestructor(int, int*) | | misc.c:219:47:219:48 | Address | Operand 'Address' is not dominated by its definition in function '$@'. | misc.c:219:5:219:26 | int assign_designated_init(someStruct*) | int assign_designated_init(someStruct*) | +| ms_try_except.cpp:9:19:9:19 | Left | Operand 'Left' is not dominated by its definition in function '$@'. | ms_try_except.cpp:2:6:2:18 | void ms_try_except(int) | void ms_try_except(int) | +| ms_try_except.cpp:9:19:9:19 | Left | Operand 'Left' is not dominated by its definition in function '$@'. | ms_try_except.cpp:2:6:2:18 | void ms_try_except(int) | void ms_try_except(int) | +| ms_try_except.cpp:19:17:19:21 | Left | Operand 'Left' is not dominated by its definition in function '$@'. | ms_try_except.cpp:2:6:2:18 | void ms_try_except(int) | void ms_try_except(int) | +| ms_try_except.cpp:19:17:19:21 | Left | Operand 'Left' is not dominated by its definition in function '$@'. | ms_try_except.cpp:2:6:2:18 | void ms_try_except(int) | void ms_try_except(int) | | static_init_templates.cpp:15:1:15:18 | Address | Operand 'Address' is not dominated by its definition in function '$@'. | static_init_templates.cpp:15:1:15:18 | void MyClass::MyClass() | void MyClass::MyClass() | | try_catch.cpp:21:9:21:9 | Address | Operand 'Address' is not dominated by its definition in function '$@'. | try_catch.cpp:19:6:19:23 | void throw_from_nonstmt(int) | 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 | int main(int, char**) | int main(int, char**) |