Skip to content
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

OpenEXR force enabling MSVC exceptions breaks unwinding through C code #1326

Open
amyspark opened this issue Jan 15, 2023 · 3 comments
Open
Labels
Bug A bug in the source code

Comments

@amyspark
Copy link

Hi,

This bug took me a bit to figure out. Ever since b4d5d86, /EHsc is forced upon all consumers of OpenEXR:

if (MSVC)
set(_openexr_extra_flags "/EHsc")
endif()

However, there's a possibly unforeseen side effect. While -fexceptions allows traversing C code (to the best of my understanding),

-fexceptions

Enable exception handling. Generates extra code needed to propagate exceptions. For some targets, this implies GCC generates frame unwind information for all functions, which can produce significant data size overhead, although it does not affect execution. If you do not specify this option, GCC enables it by default for languages like C++ that normally require exception handling, and disables it for languages like C that do not normally require it. However, you may need to enable this option when compiling C code that needs to interoperate properly with exception handlers written in C++. You may also wish to disable this option if you are compiling older C++ programs that don’t use exception handling.

/EHsc definitely does not:

c
When used with /EHs, the compiler assumes that functions declared as extern "C" never throw a C++ exception. It has no effect when used with /EHa (that is, /EHca is equivalent to /EHa). /EHc is ignored if /EHs or /EHa aren't specified.

This subtly broke my attempt to trace an error in Krita's JPEG encoder that went through C code in MSVC, since one of the dependencies of the code publicly inherited OpenEXR's INTERFACE_COMPILE_OPTIONS property.

I believe a proper fix would be to narrow it to /EHs, as that will leave downstream consumers free to disable C code traversal as well, if necessary, otherwise leaving it undisturbed.

@meshula
Copy link
Contributor

meshula commented Jan 16, 2023

I think that's right, but I'm speculating without a way to try it out. Did you try narrowing it to /EHs, and did that resolve your problem? I'm also wondering if there might be an easy way to reproduce the problem, perhaps in our test suite?

@amyspark
Copy link
Author

I think that's right, but I'm speculating without a way to try it out. Did you try narrowing it to /EHs, and did that resolve your problem? I'm also wondering if there might be an easy way to reproduce the problem, perhaps in our test suite?

Yes, this commit is the local change to Krita (I plan to push it once I've finished the branch, so commit hash is subject to change).

To reproduce it, you need to make it cross at least one DLL boundary. For instance, in this case we have three DLL boundaries:

  1. Krita's main event loop OR QtConcurrent's manager (if called from a packaged task) (C++)
  2. the JPEG plugin, which defines the throwable function to be called from libjpeg (C++)
  3. libjpeg's functions (C)

If libjpeg calls the throwable function, which is done in response to an unrecoverable error, it is possible to make MSVC catch it. However, once told to Continue, the exception will unwind past the stack frames belonging to 3, past 2's catch clause (due to /EHsc) and finally hitting 1. Given we do not expect exceptions hit the event loop, this usually leads to process termination or deadlock.

@meshula
Copy link
Contributor

meshula commented Jan 16, 2023

Ok, thanks!

cc/ @kdt3rd

@meshula meshula added the Bug A bug in the source code label Jan 16, 2023
kdesysadmin pushed a commit to KDE/krita that referenced this issue Jan 18, 2023
According to the Extra CMake Modules documentation [1],

> kde_enable_exceptions()
>
> Enables exceptions for C++ source files compiled for the CMakeLists.txt
> file in the current directory and all subdirectories.

What isn't listed there, however, is that exceptions are enabled through
the usage of -fexceptions (GCC/Clang) and -EHsc (MSVC/Intel) [2]. All
good... except that those mean slightly different things:

> -fexceptions
>
>    Enable exception handling. Generates extra code needed to propagate
>    exceptions. For some targets, this implies GCC generates frame
>    unwind information for all functions, which can produce significant
>    data size overhead, although it does not affect execution. If you do
>    not specify this option, GCC enables it by default for languages
>    like C++ that normally require exception handling, and disables it
>    for languages like C that do not normally require it. However, you
>    may need to enable this option when compiling C code that needs to
>    interoperate properly with exception handlers written in C++. You
>    may also wish to disable this option if you are compiling older C++
>    programs that don’t use exception handling.

(source: [3])

> c
> When used with /EHs, the compiler assumes that functions declared as
> extern "C" never throw a C++ exception. It has no effect when used with
> /EHa (that is, /EHca is equivalent to /EHa). /EHc is ignored if /EHs or
> /EHa aren't specified.

(source: [4])

These two put together mean that any exception thrown in a callstack
with C code in between works in Unix-y compilers, while they would
crash straight to Qt/QtConcurrent's event loop in MSVC.

The only affected piece of code is the JPEG error catcher,
introduced by Halla in 5c4c327, so
I added a file-level workaround there. A Krita-wide workaround is not
possible at the moment; all consumers of kritapigment would need the
same surgical workaround, because OpenEXR also injects /EHsc through
its exported targets. This was already reported upstream [5].

[1]: https://api.kde.org/ecm/kde-module/KDECompilerSettings.html
[2]: https://invent.kde.org/frameworks/extra-cmake-modules/-/blob/v5.101.0/kde-modules/KDECompilerSettings.cmake#L502-526
[3]: https://gcc.gnu.org/onlinedocs/gcc-7.3.0/gcc/Code-Gen-Options.html#index-fexceptions
[4]: https://learn.microsoft.com/en-us/cpp/build/reference/eh-exception-handling-model?view=msvc-170#arguments
[5]: AcademySoftwareFoundation/openexr#1326

CCBUG: 361746
CCMAIL: kimageshop@kde.org
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in the source code
Projects
None yet
Development

No branches or pull requests

2 participants