Skip to content

Adding capabilities for SEH and CPP edges in IR. #18241

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed

Conversation

bdrodes
Copy link
Contributor

@bdrodes bdrodes commented Dec 6, 2024

Phase 3 move towards supporting SEH exceptions separately from CPP exceptions. This PR differentiates the SEH and CPP edges in the IR.

@bdrodes bdrodes marked this pull request as ready for review December 6, 2024 19:01
@bdrodes bdrodes requested a review from a team as a code owner December 6, 2024 19:01
@github-actions github-actions bot added the C++ label Dec 6, 2024
@bdrodes
Copy link
Contributor Author

bdrodes commented Dec 6, 2024

@MathiasVP , would this PR require a change log?

@jketema
Copy link
Contributor

jketema commented Dec 9, 2024

Why has the raw IR significantly changed? I now see Unwind instructions in many places that then seemingly all seem to removed later as they are in unreachable blocks. Example:

@@ -2847,8 +2924,15 @@ ir.c:
 #   10|     mu10_10(int)                               = Store[?]                     : &:r10_9, r10_4
 #   11|     v11_1(void)                                = NoOp                         : 
 #    7|     v7_6(void)                                 = ReturnVoid                   : 
-#    7|     v7_7(void)                                 = AliasedUse                   : ~m?
-#    7|     v7_8(void)                                 = ExitFunction                 : 
+#-----|   Goto -> Block 1
+
+#    7|   Block 1
+#    7|     v7_7(void) = AliasedUse   : ~m?
+#    7|     v7_8(void) = ExitFunction : 
+
+#    7|   Block 2
+#    7|     v7_9(void) = Unwind : 
+#-----|   Goto -> Block 1

I also do not expect changes like the following, where suddenly branches are missing:

@@ -2970,14 +3055,12 @@ ir.c:
 #   39|     r39_8(bool) = CompareEQ         : r38_1, r39_7
 #   39|     v39_9(void) = ConditionalBranch : r39_8
 #-----|   False -> Block 3
-#-----|   True -> Block 2
 
 #   40|   Block 6
 #   40|     r40_1(glval<unknown>) = FunctionAddress[ExRaiseAccessViolation] : 
 #   40|     r40_2(int)            = Constant[1]                             : 
 #   40|     v40_3(void)           = Call[ExRaiseAccessViolation]            : func:r40_1, 0:r40_2
 #   40|     mu40_4(unknown)       = ^CallSideEffect                         : ~m?
-#-----|   C++ Exception -> Block 2
 
 #   42|   Block 7
 #   42|     v42_1(void) = NoOp       : 

@MathiasVP
Copy link
Contributor

@MathiasVP , would this PR require a change log?

Nope. What we want is a change note at the end of all of these PRs that state something along the lines of "Microsoft structured exception handling is now better supported. Queries evaluated on databases containing structured exception handling code now report more precise alerts" or something along those lines. But until we're done with all the work we don't need any change notes. So feel free to add the 'no-change-note-required' label if you can.

@bdrodes
Copy link
Contributor Author

bdrodes commented Dec 9, 2024

@MathiasVP I do not have permissions to add labels.

@bdrodes
Copy link
Contributor Author

bdrodes commented Dec 9, 2024

@jketema , as discussed in a separate DM, I found where the error was in how unwind was handled. We will now only unwind if a function is throwing a non-seh exception. I'm still investigating the missing branches.

@MathiasVP MathiasVP added the no-change-note-required This PR does not need a change note label Dec 9, 2024
@bdrodes
Copy link
Contributor Author

bdrodes commented Dec 9, 2024

@jketema the missing branches are due to not having unwind mechanics for SEH which was a conscious choice. That said, the case you pointed out is an unwind inside of a function that specifically has a try/except statement. Generally if we included unwind for SEH every function would have an unwind since SEH occurs for any load/store/call. @MathiasVP , this is a question for you as well, do you think it is appropriate to have an unwind if there is an explicit try/except or simply have as a universal rule, now unwinds for SEH? I do not believe the unwind buys anything (yet) as all function calls are assumed to potentially raise SEH anyway.

Comment on lines -2885 to +2886
# 25| v25_4(void) = Call[ExRaiseAccessViolation] : func:r25_1, 0:r25_3
# 25| mu25_5(unknown) = ^CallSideEffect : ~m?
#-----| C++ Exception -> Block 6
#-----| Goto -> Block 2
#-----| SEH Exception -> Block 5
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The goto here is unexpected, as ExRaiseAccessViolation always raises an SEH exception.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is generating a goto and an SEH not for the call, but for the Load[x], all load instructions "MAY" throw an SEH exception, so you get a goto and an exception case.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note the call doesn't occur in this block at all anymore, it is now in Block 2, which is related to your next question.

Comment on lines +2894 to +2897
# 25| Block 2
# 25| v25_4(void) = Call[ExRaiseAccessViolation] : func:r25_1, 0:r25_3
# 25| mu25_5(unknown) = ^CallSideEffect : ~m?
#-----| SEH Exception -> Block 5
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where does this block come from?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per above, the load[x] occurs, and if no exception, you will flow to block 2 for the call. The call always throws an SEH exception.

@MathiasVP
Copy link
Contributor

@jketema the missing branches are due to not having unwind mechanics for SEH which was a conscious choice. That said, the case you pointed out is an unwind inside of a function that specifically has a try/except statement. Generally if we included unwind for SEH every function would have an unwind since SEH occurs for any load/store/call. @MathiasVP , this is a question for you as well, do you think it is appropriate to have an unwind if there is an explicit try/except or simply have as a universal rule, now unwinds for SEH? I do not believe the unwind buys anything (yet) as all function calls are assumed to potentially raise SEH anyway.

The unwind exists as the handler for exceptions that reach the top of the AST (i.e., the function scope). For C++ exceptions we will definitely always want to have an Unwind instruction if a throw can reach the top AST. However, if the throw cannot reach the top of the AST because it's handled further down in the AST then no unwind instruction is currently generated, and we should continue to not generate one. For example in:

void test() {
  try {
    throw 42;
  } catch(...) { }
}

For SEH exceptions we will not generate an Unwind instruction since we will only assume that calls throw an SEH exception inside __try blocks, and thus they don't need an Unwind because they will always be handled by the __try block.

@bdrodes
Copy link
Contributor Author

bdrodes commented Dec 9, 2024

Splitting this PR up manually as suggested. Closing this PR.

@bdrodes bdrodes closed this Dec 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C++ no-change-note-required This PR does not need a change note
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants