Skip to content

Rust panics over FFI break std::exception_ptr on Windows #143623

@Fulgen301

Description

@Fulgen301

On Windows, Rust panics are implemented by assembling a C++ exception object and throwing it via _CxxThrowException.

fn _CxxThrowException(pExceptionObject: *mut c_void, pThrowInfo: *mut u8) -> !;

This exception is treated like any other regular C++ exceptions, can be caught in catch blocks, and can be retrieved via std::current_exception(). Furthermore, exceptions are mandated to be copyable by the C++ standard, and std::current_exception is permitted to copy the exception objects which is necessary on Windows as exceptions are stored on the stack and only copied to the heap when an std::exception_ptr is constructed. Thus the Rust exception panics in the copy constructor, which count as throwing a C++ exception, for which the C++ standard mandates that a std::bad_exception object is stored inside the exception pointer.

// The exception_copy function is a bit special here: it is invoked by the MSVC
// runtime under a try/catch block and the panic that we generate here will be
// used as the result of the exception copy. This is used by the C++ runtime to
// support capturing exceptions with std::exception_ptr, which we can't support
// because Box<dyn Any> isn't clonable.

...except that that panic is also a Rust panic, which will be caught by std::current_exception and dropped, and abort.

Consider the following example:

unsafe extern "C-unwind" {
    safe fn cpp_exception_foo(throws: extern "C-unwind" fn());
}

extern "C-unwind" fn do_panic() {
    panic!("hi");
}

fn main() {
    _ = std::panic::catch_unwind(|| cpp_exception_foo(do_panic));
}
#include <exception>
#include <print>

extern "C" void cpp_exception_foo(void(*callback)())
{
    try
    {
        callback();
    }
    catch(...)
    {
        std::current_exception();
        throw;
    }
}

Not only does this cause two Rust panics cannot be copied panics to be dumped to stderr, the process aborts with

fatal runtime error: Rust panics must be rethrown, aborting

Without the call std::current_exception(), it succeeds - but the copy constructor throws / panics, so the exception pointer should only have contained an std::bad_exception object in the first place! (Which too is ergonomically unpleasant, but something C++ code already has to deal with, and making Rust exceptions reference counted so that they can be stored and rethrown at a later point in time is its own can of worms.)

This breaks any sort of unwinding over FFI where the C++ side wants to create an exception pointer, even if the C++ side isn't even aware of Rust code. One might argue that Rust panics aren't exceptions and shouldn't be treated as such, but they are - they literally are implemented as throwing C++ exceptions, and the C++ language and ecosystem will treat them accordingly.

Meta

rustc --version --verbose:

rustc 1.90.0-nightly (a84ab0ce6 2025-07-06)                                                                                                                                                                                         
binary: rustc
commit-hash: a84ab0ce6c4557a2f01a3a6c3fdb0f92098db78d
commit-date: 2025-07-06
host: x86_64-pc-windows-msvc
release: 1.90.0-nightly
LLVM version: 20.1.7
Backtrace

thread 'main' panicked at src\main.rs:6:5:
hi
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

thread 'main' panicked at library\panic_unwind\src\seh.rs:289:8:
Rust panics cannot be copied
stack backtrace:
   0:     0x7ff71a9f6762 - std::backtrace_rs::backtrace::win64::trace
                               at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library\std\src\..\..\backtrace\src\backtrace\win64.rs:85
   1:     0x7ff71a9f6762 - std::backtrace_rs::backtrace::trace_unsynchronized
                               at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library\std\src\..\..\backtrace\src\backtrace\mod.rs:66
   2:     0x7ff71a9f6762 - std::sys::backtrace::_print_fmt
                               at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library\std\src\sys\backtrace.rs:66
   3:     0x7ff71a9f6762 - std::sys::backtrace::impl$0::print::impl$0::fmt
                               at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library\std\src\sys\backtrace.rs:39
   4:     0x7ff71aa016cb - core::fmt::rt::Argument::fmt
                               at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library\core\src\fmt\rt.rs:181
   5:     0x7ff71aa016cb - core::fmt::write
                               at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library\core\src\fmt\mod.rs:1446
   6:     0x7ff71a9f4e57 - std::io::default_write_fmt
                               at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library\std\src\io\mod.rs:639
   7:     0x7ff71a9f4e57 - std::io::Write::write_fmt<std::sys::stdio::windows::Stderr>
                               at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library\std\src\io\mod.rs:1914
   8:     0x7ff71a9f65a5 - std::sys::backtrace::BacktraceLock::print
                               at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library\std\src\sys\backtrace.rs:42
   9:     0x7ff71a9f7a2c - std::panicking::default_hook::closure$0
                               at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library\std\src\panicking.rs:300
  10:     0x7ff71a9f77c2 - std::panicking::default_hook
                               at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library\std\src\panicking.rs:327
  11:     0x7ff71a9f851f - std::panicking::rust_panic_with_hook
                               at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library\std\src\panicking.rs:833
  12:     0x7ff71a9f8372 - std::panicking::begin_panic_handler::closure$0
                               at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library\std\src\panicking.rs:699
  13:     0x7ff71a9f6e5f - std::sys::backtrace::__rust_end_short_backtrace<std::panicking::begin_panic_handler::closure_env$0,never$>
                               at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library\std\src\sys\backtrace.rs:168
  14:     0x7ff71a9f7fae - std::panicking::begin_panic_handler
                               at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library\std\src\panicking.rs:697
  15:     0x7ff71aa06161 - core::panicking::panic_fmt
                               at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library\core\src\panicking.rs:75
  16:     0x7ff71aa0604c - panic_unwind::imp::exception_copy
                               at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library\panic_unwind\src\seh.rs:281
  17:     0x7ffe1f55228e - <unknown>
  18:     0x7ffe1f55200c - <unknown>
  19:     0x7ffe1f552511 - __ExceptionPtrCurrentException
  20:     0x7ff71aa04842 - std::current_exception
                               at C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\include\exception:318
  21:     0x7ff71aa066a6 - cpp_exception_foo$catch$0
                               at C:\Users\tokgeo\source\repos\uncaught_exceptions\src\main.cpp:12
  22:     0x7ffe4cd541d0 - _CxxFrameHandler4
  23:     0x7ffe4cd5258f - <unknown>
  24:     0x7ffe63646c56 - RtlCaptureContext2
  25:     0x7ff71aa047ad - cpp_exception_foo
                               at C:\Users\tokgeo\source\repos\uncaught_exceptions\src\main.cpp:8
  26:     0x7ff71a9f11a0 - uncaught_exceptions::main::closure$0
                               at C:\Users\tokgeo\source\repos\uncaught_exceptions\src\main.rs:10
  27:     0x7ff71a9f1318 - std::panicking::try::do_call<uncaught_exceptions::main::closure_env$0,tuple$<> >
                               at C:\Users\tokgeo\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\panicking.rs:589
  28:     0x7ff71a9f10b3 - std::panic::catch_unwind<uncaught_exceptions::main::closure_env$0,tuple$<> >
  29:     0x7ff71a9f1046 - std::panicking::try
                               at C:\Users\tokgeo\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\panicking.rs:552
  30:     0x7ff71a9f1046 - std::panic::catch_unwind<uncaught_exceptions::main::closure_env$0,tuple$<> >
                               at C:\Users\tokgeo\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\panic.rs:359
  31:     0x7ff71a9f13d9 - uncaught_exceptions::main
                               at C:\Users\tokgeo\source\repos\uncaught_exceptions\src\main.rs:10
  32:     0x7ff71a9f144b - core::ops::function::FnOnce::call_once<void (*)(),tuple$<> >
                               at C:\Users\tokgeo\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\ops\function.rs:250
  33:     0x7ff71a9f100e - core::hint::black_box
                               at C:\Users\tokgeo\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\hint.rs:482
  34:     0x7ff71a9f100e - std::sys::backtrace::__rust_begin_short_backtrace<void (*)(),tuple$<> >
                               at C:\Users\tokgeo\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\sys\backtrace.rs:152
  35:     0x7ff71a9f1181 - std::rt::lang_start::closure$0<tuple$<> >
                               at C:\Users\tokgeo\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\rt.rs:199
  36:     0x7ff71a9f315c - std::rt::lang_start_internal::closure$0
                               at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library\std\src\rt.rs:168
  37:     0x7ff71a9f315c - std::panicking::try::do_call
                               at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library\std\src\panicking.rs:589
  38:     0x7ff71a9f315c - std::panicking::try
                               at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library\std\src\panicking.rs:552
  39:     0x7ff71a9f315c - std::panic::catch_unwind
                               at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library\std\src\panic.rs:359
  40:     0x7ff71a9f315c - std::rt::lang_start_internal
                               at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library\std\src\rt.rs:164
  41:     0x7ff71a9f116a - std::rt::lang_start<tuple$<> >
                               at C:\Users\tokgeo\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\rt.rs:198
  42:     0x7ff71a9f1419 - main
  43:     0x7ff71aa04a68 - invoke_main
                               at D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:78
  44:     0x7ff71aa04a68 - __scrt_common_main_seh
                               at D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288
  45:     0x7ffe6296e8d7 - BaseThreadInitThunk
  46:     0x7ffe635bc5dc - RtlUserThreadStart

thread 'main' panicked at library\panic_unwind\src\seh.rs:289:8:
Rust panics cannot be copied
stack backtrace:
   0:     0x7ff71a9f6762 - std::backtrace_rs::backtrace::win64::trace
                               at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library\std\src\..\..\backtrace\src\backtrace\win64.rs:85
   1:     0x7ff71a9f6762 - std::backtrace_rs::backtrace::trace_unsynchronized
                               at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library\std\src\..\..\backtrace\src\backtrace\mod.rs:66
   2:     0x7ff71a9f6762 - std::sys::backtrace::_print_fmt
                               at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library\std\src\sys\backtrace.rs:66
   3:     0x7ff71a9f6762 - std::sys::backtrace::impl$0::print::impl$0::fmt
                               at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library\std\src\sys\backtrace.rs:39
   4:     0x7ff71aa016cb - core::fmt::rt::Argument::fmt
                               at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library\core\src\fmt\rt.rs:181
   5:     0x7ff71aa016cb - core::fmt::write
                               at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library\core\src\fmt\mod.rs:1446
   6:     0x7ff71a9f4e57 - std::io::default_write_fmt
                               at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library\std\src\io\mod.rs:639
   7:     0x7ff71a9f4e57 - std::io::Write::write_fmt<std::sys::stdio::windows::Stderr>
                               at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library\std\src\io\mod.rs:1914
   8:     0x7ff71a9f65a5 - std::sys::backtrace::BacktraceLock::print
                               at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library\std\src\sys\backtrace.rs:42
   9:     0x7ff71a9f7a2c - std::panicking::default_hook::closure$0
                               at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library\std\src\panicking.rs:300
  10:     0x7ff71a9f77c2 - std::panicking::default_hook
                               at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library\std\src\panicking.rs:327
  11:     0x7ff71a9f851f - std::panicking::rust_panic_with_hook
                               at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library\std\src\panicking.rs:833
  12:     0x7ff71a9f8372 - std::panicking::begin_panic_handler::closure$0
                               at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library\std\src\panicking.rs:699
  13:     0x7ff71a9f6e5f - std::sys::backtrace::__rust_end_short_backtrace<std::panicking::begin_panic_handler::closure_env$0,never$>
                               at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library\std\src\sys\backtrace.rs:168
  14:     0x7ff71a9f7fae - std::panicking::begin_panic_handler
                               at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library\std\src\panicking.rs:697
  15:     0x7ff71aa06161 - core::panicking::panic_fmt
                               at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library\core\src\panicking.rs:75
  16:     0x7ff71aa0604c - panic_unwind::imp::exception_copy
                               at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library\panic_unwind\src\seh.rs:281
  17:     0x7ffe1f55228e - <unknown>
  18:     0x7ffe1f59e651 - std::basic_ostream<wchar_t,std::char_traits<wchar_t> >::write
  19:     0x7ffe4cd541d0 - _CxxFrameHandler4
  20:     0x7ffe4cd5258f - <unknown>
  21:     0x7ffe63646c56 - RtlCaptureContext2
  22:     0x7ffe1f55200c - <unknown>
  23:     0x7ffe1f552511 - __ExceptionPtrCurrentException
  24:     0x7ff71aa04842 - std::current_exception
                               at C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\include\exception:318
  25:     0x7ff71aa066a6 - cpp_exception_foo$catch$0
                               at C:\Users\tokgeo\source\repos\uncaught_exceptions\src\main.cpp:12
  26:     0x7ffe4cd541d0 - _CxxFrameHandler4
  27:     0x7ffe4cd5258f - <unknown>
  28:     0x7ffe63646c56 - RtlCaptureContext2
  29:     0x7ff71aa047ad - cpp_exception_foo
                               at C:\Users\tokgeo\source\repos\uncaught_exceptions\src\main.cpp:8
  30:     0x7ff71a9f11a0 - uncaught_exceptions::main::closure$0
                               at C:\Users\tokgeo\source\repos\uncaught_exceptions\src\main.rs:10
  31:     0x7ff71a9f1318 - std::panicking::try::do_call<uncaught_exceptions::main::closure_env$0,tuple$<> >
                               at C:\Users\tokgeo\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\panicking.rs:589
  32:     0x7ff71a9f10b3 - std::panic::catch_unwind<uncaught_exceptions::main::closure_env$0,tuple$<> >
  33:     0x7ff71a9f1046 - std::panicking::try
                               at C:\Users\tokgeo\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\panicking.rs:552
  34:     0x7ff71a9f1046 - std::panic::catch_unwind<uncaught_exceptions::main::closure_env$0,tuple$<> >
                               at C:\Users\tokgeo\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\panic.rs:359
  35:     0x7ff71a9f13d9 - uncaught_exceptions::main
                               at C:\Users\tokgeo\source\repos\uncaught_exceptions\src\main.rs:10
  36:     0x7ff71a9f144b - core::ops::function::FnOnce::call_once<void (*)(),tuple$<> >
                               at C:\Users\tokgeo\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\ops\function.rs:250
  37:     0x7ff71a9f100e - core::hint::black_box
                               at C:\Users\tokgeo\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\hint.rs:482
  38:     0x7ff71a9f100e - std::sys::backtrace::__rust_begin_short_backtrace<void (*)(),tuple$<> >
                               at C:\Users\tokgeo\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\sys\backtrace.rs:152
  39:     0x7ff71a9f1181 - std::rt::lang_start::closure$0<tuple$<> >
                               at C:\Users\tokgeo\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\rt.rs:199
  40:     0x7ff71a9f315c - std::rt::lang_start_internal::closure$0
                               at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library\std\src\rt.rs:168
  41:     0x7ff71a9f315c - std::panicking::try::do_call
                               at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library\std\src\panicking.rs:589
  42:     0x7ff71a9f315c - std::panicking::try
                               at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library\std\src\panicking.rs:552
  43:     0x7ff71a9f315c - std::panic::catch_unwind
                               at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library\std\src\panic.rs:359
  44:     0x7ff71a9f315c - std::rt::lang_start_internal
                               at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library\std\src\rt.rs:164
  45:     0x7ff71a9f116a - std::rt::lang_start<tuple$<> >
                               at C:\Users\tokgeo\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\rt.rs:198
  46:     0x7ff71a9f1419 - main
  47:     0x7ff71aa04a68 - invoke_main
                               at D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:78
  48:     0x7ff71aa04a68 - __scrt_common_main_seh
                               at D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288
  49:     0x7ffe6296e8d7 - BaseThreadInitThunk
  50:     0x7ffe635bc5dc - RtlUserThreadStart
fatal runtime error: Rust panics must be rethrown, aborting
error: process didn't exit successfully: `target\debug\uncaught_exceptions.exe` (exit code: 0xc0000409, STATUS_STACK_BUFFER_OVERRUN)

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-bugCategory: This is a bug.O-windowsOperating system: Windowsneeds-triageThis issue may need triage. Remove it if it has been sufficiently triaged.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions