Skip to content

Win: Use exceptions with empty data for SEH panic exception copies instead of a new panic #143651

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

Merged
merged 1 commit into from
Jul 10, 2025

Conversation

Fulgen301
Copy link
Contributor

@Fulgen301 Fulgen301 commented Jul 8, 2025

For unwinding with SEH, we currently construct a C++ exception with the panic data. Being a regular C++ exception, it interacts with the C++ exception handling machinery and can be retrieved via std::current_exception, which needs to copy the exception. We can't support that, so we panic, which throws another exception, which the C++ runtime tries to copy and store into the exception_ptr, which panics again, which causes the C++ runtime to store a bad_exception instance.

However, this doesn't work because the panics thrown by the copy function will be dropped without being rethrown, and causes unnecessary log spam in stderr. Fix this by directly throwing an exception without data, which doesn't cause log spam and can be dropped without being rethrown.

Fixes #143623.

This also happens to be the solution @dpaoliello suggested, though I'm not sure how to handle the commit credit attribution.

@rustbot
Copy link
Collaborator

rustbot commented Jul 8, 2025

r? @jhpratt

rustbot has assigned @jhpratt.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Jul 8, 2025
Copy link
Contributor

@dpaoliello dpaoliello left a comment

Choose a reason for hiding this comment

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

Minor typo, but LGTM

@ChrisDenton
Copy link
Member

Could you squish your commits?

@ChrisDenton ChrisDenton assigned ChrisDenton and unassigned jhpratt Jul 8, 2025
instead of a new panic

For unwinding with SEH, we currently construct a C++ exception with the
panic data. Being a regular C++ exception, it interacts with the C++
exception handling machinery and can be retrieved via
`std::current_exception`, which needs to copy the exception. We can't
support that, so we panic, which throws another exception, which the
C++ runtime tries to copy and store into the exception_ptr, which panics
again, which causes the C++ runtime to store a `bad_exception` instance.

However, this doesn't work because the panics thrown by the copy
function will be dropped without being rethrown, and causes unnecessary
log spam in stderr. Fix this by directly throwing an exception without
data, which doesn't cause log spam and can be dropped without being
rethrown.
@Fulgen301 Fulgen301 force-pushed the seh-exception-ptr branch from 888eb40 to 96cdbb9 Compare July 8, 2025 20:08
@ChrisDenton
Copy link
Member

ChrisDenton commented Jul 8, 2025

Thanks!

@ChrisDenton
Copy link
Member

Let's try that again...

@bors r+ rollup

@bors
Copy link
Collaborator

bors commented Jul 8, 2025

📌 Commit 96cdbb9 has been approved by ChrisDenton

It is now in the queue for this repository.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jul 8, 2025
jhpratt added a commit to jhpratt/rust that referenced this pull request Jul 9, 2025
…isDenton

Win: Use exceptions with empty data for SEH panic exception copies instead of a new panic

For unwinding with SEH, we currently construct a C++ exception with the panic data. Being a regular C++ exception, it interacts with the C++ exception handling machinery and can be retrieved via `std::current_exception`, which needs to copy the exception. We can't support that, so we panic, which throws another exception, which the C++ runtime tries to copy and store into the exception_ptr, which panics again, which causes the C++ runtime to store a `bad_exception` instance.

However, this doesn't work because the panics thrown by the copy function will be dropped without being rethrown, and causes unnecessary log spam in stderr. Fix this by directly throwing an exception without data, which doesn't cause log spam and can be dropped without being rethrown.

Fixes rust-lang#143623.

This also happens to be the solution `@dpaoliello` suggested, though I'm not sure how to handle the commit credit attribution.
bors added a commit that referenced this pull request Jul 9, 2025
Rollup of 11 pull requests

Successful merges:

 - #143177 (Remove false label when `self` resolve failure does not relate to macro)
 - #143339 (Respect endianness correctly in CheckEnums test suite)
 - #143426 (clippy fix: indentation)
 - #143499 (Don't call `predicates_of` on a dummy obligation cause's body id)
 - #143520 (Fix perf regression caused by tracing)
 - #143532 (More carefully consider span context when suggesting remove `&mut`)
 - #143606 (configure.py: Write last key in each section)
 - #143632 (fix: correct parameter names in LLVMRustBuildMinNum and LLVMRustBuildMaxNum FFI declarations)
 - #143644 (Add triagebot stdarch mention ping)
 - #143651 (Win: Use exceptions with empty data for SEH panic exception copies instead of a new panic)
 - #143660 (Disable docs for `compiler-builtins` and `sysroot`)

r? `@ghost`
`@rustbot` modify labels: rollup
@purplesyringa
Copy link
Contributor

This also happens to be the solution @dpaoliello suggested

For context, that's not quite the case... Unless I'm missing something, he recommended exception_cleanup to disarm the discard check on the original panic. Your solution doesn't, and I think that's more correct.

@dpaoliello
Copy link
Contributor

For context, that's not quite the case... Unless I'm missing something, he recommended exception_cleanup to disarm the discard check on the original panic. Your solution doesn't, and I think that's more correct.

Actually, that's a good point: are we still going to see a panic because the original panic wasn't rethrown (i.e., in exception_cleanup it will see that the original panic's data is not None)?

@Fulgen301
Copy link
Contributor Author

Correct. The panic exceptions with data == None only serve as sentinel exceptions so that the exception_ptrmachinery stores a bad_exception instance instead. The original exception still has to be rethrown. We can't do a fake copy and silence the original drop check, because Rust panics must not be handled by C++.

If we wanted to support rethrowing via the exception_ptr, we'd have to internally refcount the data, but still keep a drop check so that either the original exception on the stack or a copy in an exception_ptr was rethrown. This isn't a good fix for three reasons:

  • It adds the cost of an Arc or equivalent for to every panic.
  • It wouldn't be as clear cut of a bugfix as this is; C++ code has to deal with the possiblity of std::current_exception() returning a std::bad_exception ptr because the standard explicitely declared that to be the fallback for exception copy failures, whereas the bug here was a misguided assumption in code that already existed (threw an exception in the copy function that had to be rethrown because it was a panic).
  • It'd mean the C++ side could store a copy of the panic exception and rethrow it later. We can detect that as std::rethrow_exception would copy the exception again, but we can't reuse the exception or throw a panic exception in that case, because unwinding might return to Rust code which would get a panic out of nowhere, so we'd have to add another throw info for a primitive or a different exception type. And it's a lot of code to support C++ storing a, from Rust's point of view, implementation detail with no stability guarantees, all to avoid a scenario that C++ code already has to deal with.

tgross35 added a commit to tgross35/rust that referenced this pull request Jul 10, 2025
…isDenton

Win: Use exceptions with empty data for SEH panic exception copies instead of a new panic

For unwinding with SEH, we currently construct a C++ exception with the panic data. Being a regular C++ exception, it interacts with the C++ exception handling machinery and can be retrieved via `std::current_exception`, which needs to copy the exception. We can't support that, so we panic, which throws another exception, which the C++ runtime tries to copy and store into the exception_ptr, which panics again, which causes the C++ runtime to store a `bad_exception` instance.

However, this doesn't work because the panics thrown by the copy function will be dropped without being rethrown, and causes unnecessary log spam in stderr. Fix this by directly throwing an exception without data, which doesn't cause log spam and can be dropped without being rethrown.

Fixes rust-lang#143623.

This also happens to be the solution `@dpaoliello` suggested, though I'm not sure how to handle the commit credit attribution.
bors added a commit that referenced this pull request Jul 10, 2025
Rollup of 9 pull requests

Successful merges:

 - #141996 (Fix `proc_macro::Ident`'s handling of `$crate`)
 - #142911 (Remove support for dynamic allocas)
 - #142950 (mbe: Rework diagnostics for metavariable expressions)
 - #143270 (tests/codegen/enum/enum-match.rs: accept negative range attribute)
 - #143298 (`tests/ui`: A New Order [23/N])
 - #143398 (tidy: add support for `--extra-checks=auto:` feature)
 - #143632 (fix: correct parameter names in LLVMRustBuildMinNum and LLVMRustBuildMaxNum FFI declarations)
 - #143644 (Add triagebot stdarch mention ping)
 - #143651 (Win: Use exceptions with empty data for SEH panic exception copies instead of a new panic)

r? `@ghost`
`@rustbot` modify labels: rollup
bors added a commit that referenced this pull request Jul 10, 2025
Rollup of 9 pull requests

Successful merges:

 - #143446 (use `--dynamic-list` for exporting executable symbols)
 - #143590 (Fix weird rustdoc output when single and glob reexport conflict on a name)
 - #143599 (emit `.att_syntax` when global/naked asm use that option)
 - #143615 (Fix handling of no_std targets in `doc::Std` step)
 - #143632 (fix: correct parameter names in LLVMRustBuildMinNum and LLVMRustBuildMaxNum FFI declarations)
 - #143640 (Constify `Fn*` traits)
 - #143651 (Win: Use exceptions with empty data for SEH panic exception copies instead of a new panic)
 - #143660 (Disable docs for `compiler-builtins` and `sysroot`)
 - #143665 ([rustdoc-json] Add tests for `#[doc(hidden)]` handling of items.)

r? `@ghost`
`@rustbot` modify labels: rollup
@bors bors merged commit 92f9480 into rust-lang:master Jul 10, 2025
11 checks passed
@rustbot rustbot added this to the 1.90.0 milestone Jul 10, 2025
rust-timer added a commit that referenced this pull request Jul 10, 2025
Rollup merge of #143651 - Fulgen301:seh-exception-ptr, r=ChrisDenton

Win: Use exceptions with empty data for SEH panic exception copies instead of a new panic

For unwinding with SEH, we currently construct a C++ exception with the panic data. Being a regular C++ exception, it interacts with the C++ exception handling machinery and can be retrieved via `std::current_exception`, which needs to copy the exception. We can't support that, so we panic, which throws another exception, which the C++ runtime tries to copy and store into the exception_ptr, which panics again, which causes the C++ runtime to store a `bad_exception` instance.

However, this doesn't work because the panics thrown by the copy function will be dropped without being rethrown, and causes unnecessary log spam in stderr. Fix this by directly throwing an exception without data, which doesn't cause log spam and can be dropped without being rethrown.

Fixes #143623.

This also happens to be the solution ``@dpaoliello`` suggested, though I'm not sure how to handle the commit credit attribution.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-libs Relevant to the library team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Rust panics over FFI break std::exception_ptr on Windows
7 participants