Closed djrecipe closed 1 year ago
Is not a bug, chromium code setup own signal handlers like it should. As workaround you can store current (.net) handler and restore it back after CefInitialize. Also keep in mind what CEF/Chromium doesn't use exceptions and will not throw them, and you must not throw exceptions from any CEF's handler.
@dmitry-azaraev Thanks for the prompt response.
you must not throw exceptions from any CEF's handler
Of course there are no uncaught exceptions thrown within any of our CefApp
or CefProxy
implementations, if that is what you mean.
@dmitry-azaraev Regarding the first item, would you be able to point me to any resource that might explain how to get started with this (store and restore a signal handler)?
.NET Interop works on Windows machines without the aformentioned storing & receiving of a signal handler, and I couldn't find anything about this requirement in the docs for CEF. I know that Unix .NET/native interop is not as well implemented as it is for Windows, and I am happy to oblige to any further requirements needed to be able to catch exceptions in .NET after invoking CefInitialize
in native code - I could just use help getting pointed in the right direction.
I guess you need try to use sigaction
call (SIGSEGV), or similar calls.
Similar topic from past https://gitlab.com/xiliumhq/chromiumembedded/cefglue/-/issues/29 which I remember.
Also topic probably was discussed at ceforum few times. I'm personally done something similar far earlier but don't remember details.
@dmitry-azaraev Thanks mate, I'll give it a try and see if I can post the results, much appreciated.
@dmitry-azaraev lo-and-behold, you were right
It's not pretty, but at a glance it's something like
struct sigaction sigabrt;
struct sigaction sigfpe;
struct sigaction sigill;
struct sigaction sigint;
struct sigaction sigsegv;
struct sigaction sigterm;
memset(&sigabrt, 0, sizeof(sigabrt));
memset(&sigfpe, 0, sizeof(sigfpe));
memset(&sigill, 0, sizeof(sigill));
memset(&sigint, 0, sizeof(sigint));
memset(&sigsegv, 0, sizeof(sigsegv));
memset(&sigterm, 0, sizeof(sigterm));
sigaction(SIGABRT, 0, &sigabrt);
sigaction(SIGFPE, 0, &sigfpe);
sigaction(SIGILL, 0, &sigill);
sigaction(SIGINT, 0, &sigint);
sigaction(SIGSEGV, 0, &sigsegv);
sigaction(SIGTERM, 0, &sigterm);
...
CefInitialize(args, cef_settings, &proxy, NULL);
...
// restore handlers
struct sigaction sigabrtcef;
struct sigaction sigfpecef;
struct sigaction sigillcef;
struct sigaction sigintcef;
struct sigaction sigsegvcef;
struct sigaction sigtermcef;
memset(&sigabrtcef, 0, sizeof(sigabrtcef));
memset(&sigfpecef, 0, sizeof(sigfpecef));
memset(&sigillcef, 0, sizeof(sigillcef));
memset(&sigintcef, 0, sizeof(sigintcef));
memset(&sigsegvcef, 0, sizeof(sigsegvcef));
memset(&sigtermcef, 0, sizeof(sigtermcef));
sigaction(SIGABRT, &sigabrt, &sigabrtcef);
sigaction(SIGFPE, &sigfpe, &sigfpecef);
sigaction(SIGILL, &sigill, &sigillcef);
sigaction(SIGINT, &sigint, &sigintcef);
sigaction(SIGSEGV, &sigsegv, &sigsegvcef);
sigaction(SIGTERM, &sigterm, &sigtermcef);
Not sure if it's necessary for all the signals, but I'll play around with it.
In addition to this, based on information from the issue you ilnked, I might need to restore the CEF handlers each time I need to invoke native code, but that shouldn't be an issue for me.
Thanks again.
No, is misunderstanding probably. Mostly cef/chromium setup own handlers to perform diagnostics (e.g. collect crashdumps), so it generally should be one-shot action from his side, and by so it doesn't need to restore/store them for every call. However, eventually you might want to have both: native/chromium dumps and .net exceptions and for this i have no ready to use answer, and to make it work both together i guess would require extending .net runtime by providing stack decoder/prober so you can identify if you should chain to previous (chromium) handler, for example.
Keep in mind what signals are process-wide, so in general case you can't just swap handlers without affecting all threads. One notable threads is .net thread pool and cef's threadpool. However, modern .net handler probably good enough for generating native minidumps, but it too like convert crash to exception during pinvoke, instead of just crash, like it should be. So it not always actually handy. But if you doesnt focus on cef internals or don't use cef crashdumps, then is probably not very important.
I've restored the dotnet signal handlers as discussed, and now I can once again happily de-reference null in .NET try/catch clause, even after initializing CEF in native Linux.
I will mention that breakpoints still don't seem to work, even though they should be covered under SIGTRAP. However, this is not a big concern for me at this time.
Anyways, thanks again for your insight! Feel free to close this issue if you'd like.
Describe the bug Calling
CefInitialize()
on Linux machines prevents usage of .NET interop. While the application will appear to continue to function normally, user-thrown exceptions (including SIGTRAP/breakpoints) cannot be caught, due to stack corruption/broken stack-unwinding mechanism.To Reproduce https://github.com/iron-software/CefStackUnwindingBug
I have created a repo which exposes
CefInitialize
via interop, then invokes the interop method. This repo is designed to be as concise as possible for the convenience of those who'd like to try and reproduce this issue.The main demonstration is the inability to catch a
NullReferenceException
in .NET after callingCefInitialize()
via interop on Linux machines.Expected behavior Users should be able to continue to catch user-thrown exceptions and/or breakpoints, even after calling CefInitialize() via interop on Linux machines.
Screenshots Please see the README and comments in https://github.com/iron-software/CefStackUnwindingBug
Versions (please complete the following information):
Additional context It's a bit of a unique use case, but not that far in left field:
CefInitialize()
and expose it in a shared libraryI've been using CEF for a few years now as part of a commercial software component, and finally got around to creating a repo which demonstrates this issue.
My hunch is that
CefInitialize()
is calling somejmp
instruction or something that subsequently breaks stack-unwinding after returning to managed code. Thus you will only ever see an issue when trying to catch a user-thrown exception or by trying to set a breakpoint.Please refer to https://github.com/iron-software/CefStackUnwindingBug for more information. Thank you for your time!