Windows unwind safety with abort-on-panic

Working with a codebase that sets panic = "abort", I am unsure
about the behavior of that in Windows. Will the abort immediately
terminate the process just as it does on Linux or is there a risk of
unwinding to take place?

I know only little about Windows, but at least as far as C++ is
concerned I understand it handles aborts through SEH, and thus
unwinding.

I’m asking cause of an FFI layer that is part of the codebase which
provides bindings for other languages. I want to be absolutely certain
that no unwinding will happen across the FFI boundary. Instant aborts
are fine (customers seem to be ok with it), but UB is not.

With panic=abort the fast fail mechanism introduced in Windows 8 is used to abort without unwinding. On earlier windows versions it still aborts, but may run exception handlers.

3 Likes

Thanks that’s good to know. Is the “fast fail” mechanism a Rust thing or a Windows thing?

EDIT: I think I found it, __fastfail appears to be an abort(3) style Windows API.

Rust doesn't try to perform such tricks, and in general, Rust considers it UB to unwind across the FFI boundary. Thus, this must be a Windows thing, not a Rust problem.

in general, Rust considers it UB to unwind across the FFI boundary.

Sure, hence my question.

Thus, this must be a Windows thing, not a Rust problem.

That __fastfail method, judging from the comments in
library/panic_abort/src/lib.rs, seems to work pretty much
like an abort on Linux. Older Windowses do pose a problem however
as the stack is being unwound even with panic = "abort".

In theory (I don't have a Win7 machine to test on) an abort-triggered unwind would be a structured exception and Rust code would be compiled with the equivalent of /EHsc, meaning that C++ try/catch and unwind tables do not interact with SEH exceptions.

SEH should only be interacted with via the MSVC-extension __try/__except/__finally blocks. According to the documentation example, C++ destructors are run when unwinding SEH if /EHa is used but not if /EHsc is used.

This may be on the Rust runtime to ask whether a foreign unwind is "forced" (asynchronous) or "unforced" (synchronous) before running destructors. (I seem to recall longjmp being implemented via SEH on Windows and running destructors / getting caught by nounwind guards, so this is likely the case.) So this "how things should work" may be meaningless.

Windows 7 is essentially a tier 2 target at this point (and fully unsupported by Microsoft as of January 2023), so it isn't a huge priority to fix. AIUI, all other cases of SEH are only triggered by UB.

1 Like