diff --git a/CHANGELOG.md b/CHANGELOG.md index c74092ca7..354890e33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ - Add `sentry_scope_remove_fingerprint` to remove a fingerprint set on a scope, matching the global `sentry_remove_fingerprint`. ([#1932](https://github.com/getsentry/sentry-native/pull/1932)) +**Fixes**: + +- Crashpad: fix a crash when calling `sentry_init` before C++ dynamic initializers have run. ([#1930](https://github.com/getsentry/sentry-native/issues/1930), [mini_chromium#8](https://github.com/getsentry/mini_chromium/pull/8)) + ## 0.16.1 **Features**: diff --git a/CMakeLists.txt b/CMakeLists.txt index 0713824d4..12597597e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1003,6 +1003,7 @@ endif() if(SENTRY_BUILD_TESTS) add_subdirectory(tests/unit) add_subdirectory(tests/fixtures/crash_reporter) + add_subdirectory(tests/fixtures/early_init) add_subdirectory(tests/fixtures/screenshot) if(WIN32 AND NOT XBOX) add_subdirectory(tests/fixtures/appx) diff --git a/external/crashpad b/external/crashpad index aae505d3d..46b01fd75 160000 --- a/external/crashpad +++ b/external/crashpad @@ -1 +1 @@ -Subproject commit aae505d3daf73e8a48136ccc7398663f16096712 +Subproject commit 46b01fd750285a3b85bd1d4d98b606321f788069 diff --git a/tests/cmake.py b/tests/cmake.py index 5d6287f17..c462af116 100644 --- a/tests/cmake.py +++ b/tests/cmake.py @@ -94,6 +94,7 @@ def destroy(self): "sentry_test_unit", "libsentry.dylib" if sys.platform == "darwin" else "libsentry.so", "sentry-crash", + "sentry_early_init", ] cmd = [ os.environ.get("LLVM_COV", "llvm-cov"), diff --git a/tests/fixtures/early_init/CMakeLists.txt b/tests/fixtures/early_init/CMakeLists.txt new file mode 100644 index 000000000..b8047f680 --- /dev/null +++ b/tests/fixtures/early_init/CMakeLists.txt @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 3.10) +project(sentry_early_init LANGUAGES CXX) + +add_executable(sentry_early_init early_init.cpp) +target_link_libraries(sentry_early_init PRIVATE sentry) diff --git a/tests/fixtures/early_init/early_init.cpp b/tests/fixtures/early_init/early_init.cpp new file mode 100644 index 000000000..9052455b5 --- /dev/null +++ b/tests/fixtures/early_init/early_init.cpp @@ -0,0 +1,28 @@ +#include + +static int +do_early_init() +{ + sentry_options_t *options = sentry_options_new(); + sentry_options_set_debug(options, true); + return sentry_init(options); +} + +struct EarlyInit { + int result = do_early_init(); +}; + +#if defined(_MSC_VER) +// call sentry_init() as early as possible, before other static initializers +# pragma init_seg(lib) +#elif defined(__GNUC__) || defined(__clang__) +__attribute__((init_priority(101))) +#endif +static EarlyInit early_init; + +int +main() +{ + sentry_close(); + return early_init.result; +} diff --git a/tests/test_integration_crashpad.py b/tests/test_integration_crashpad.py index ac1c50f41..422b58f40 100644 --- a/tests/test_integration_crashpad.py +++ b/tests/test_integration_crashpad.py @@ -1134,3 +1134,21 @@ def test_crashpad_restart_on_crash(cmake, httpserver): assert len(httpserver.log) == 2 for req in httpserver.log: assert_crashpad_upload(req[0]) + + +def test_crashpad_early_init(cmake): + tmp_path = cmake( + ["sentry_early_init"], + { + "SENTRY_BACKEND": "crashpad", + "SENTRY_TRANSPORT": "none", + "BUILD_SHARED_LIBS": "OFF", + }, + ) + + result = run( + tmp_path, + "sentry_early_init", + [], + ) + assert result.returncode == 0 diff --git a/tests/test_integration_http.py b/tests/test_integration_http.py index 2aab22131..d923e1177 100644 --- a/tests/test_integration_http.py +++ b/tests/test_integration_http.py @@ -1431,3 +1431,29 @@ def test_restart_on_crash(cmake, httpserver, backend): assert_breakpad_crash(envelope) else: assert False + + +@pytest.mark.parametrize( + "backend", + [ + "inproc", + pytest.param( + "breakpad", + marks=pytest.mark.skipif( + not has_breakpad, reason="breakpad backend not available" + ), + ), + ], +) +def test_early_init(cmake, backend): + tmp_path = cmake( + ["sentry_early_init"], + { + "SENTRY_BACKEND": backend, + "SENTRY_TRANSPORT": "none", + "BUILD_SHARED_LIBS": "OFF", + }, + ) + + result = run(tmp_path, "sentry_early_init", []) + assert result.returncode == 0 diff --git a/tests/test_integration_native.py b/tests/test_integration_native.py index 92b29c185..4eb25c96c 100644 --- a/tests/test_integration_native.py +++ b/tests/test_integration_native.py @@ -1193,3 +1193,21 @@ def test_native_replay_orphan_not_flushed(cmake, httpserver): assert not is_replay_envelope(req.get_data()) assert (replays / f"replay-{REPLAY_ID}.mp4").exists() assert (replays / f"replay-{REPLAY_ID}.json").exists() + + +def test_native_early_init(cmake): + tmp_path = cmake( + ["sentry_early_init"], + { + "SENTRY_BACKEND": "native", + "SENTRY_TRANSPORT": "none", + "BUILD_SHARED_LIBS": "OFF", + }, + ) + + result = run( + tmp_path, + "sentry_early_init", + [], + ) + assert result.returncode == 0