From 1278d3128177109cf7578eff457614b3eddceaaa Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Fri, 8 May 2026 17:59:43 +0200 Subject: [PATCH 01/18] fix(native): capture WER crash exceptions --- CHANGELOG.md | 4 + CMakeLists.txt | 36 +++ .../native/minidump/sentry_minidump_windows.c | 22 +- src/backends/native/sentry_wer.c | 240 ++++++++++++++++++ src/backends/native/sentry_wer.def | 6 + src/backends/sentry_backend_native.c | 103 ++++++++ tests/cmake.py | 1 + tests/conftest.py | 12 +- tests/test_integration_crashpad.py | 8 +- tests/test_integration_native.py | 35 +++ tests/test_integration_screenshot.py | 4 +- 11 files changed, 453 insertions(+), 18 deletions(-) create mode 100644 src/backends/native/sentry_wer.c create mode 100644 src/backends/native/sentry_wer.def diff --git a/CHANGELOG.md b/CHANGELOG.md index b85251c3a6..34edd2286a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ - Auto-populate `event.user.id` with a persistent per-installation UUID when no explicit user ID is set. ([#1661](https://github.com/getsentry/sentry-native/pull/1661)) +**Fixes**: + +- Native/Windows: capture fast-fail and stack buffer overrun crashes via WER. + ## 0.14.0 **Breaking / Important behavior changes**: diff --git a/CMakeLists.txt b/CMakeLists.txt index cf2fb45b0f..9655218688 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -767,6 +767,27 @@ elseif(SENTRY_BACKEND_NATIVE) # Native backend sources and configuration are in src/CMakeLists.txt # The native backend requires C11 for atomics (set in src/CMakeLists.txt) + if(WIN32) + add_library(sentry-wer SHARED + src/backends/native/sentry_wer.c + src/backends/native/sentry_wer.def + ) + target_include_directories(sentry-wer PRIVATE + ${PROJECT_SOURCE_DIR}/include + ${PROJECT_SOURCE_DIR}/src + ${PROJECT_SOURCE_DIR}/src/backends/native + ) + target_link_libraries(sentry-wer PRIVATE wer) + set_property(TARGET sentry-wer PROPERTY PREFIX "") # ensure MINGW doesn't prefix "lib" to dll name + set_property(TARGET sentry-wer PROPERTY DEBUG_POSTFIX "") # prevent CMAKE_DEBUG_POSTFIX from being applied + if(SENTRY_BUILD_RUNTIMESTATIC AND MSVC) + set_property(TARGET sentry-wer PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + endif() + if(SENTRY_BUILD_SHARED_LIBS) + sentry_add_version_resource(sentry-wer "Native WER Module") + endif() + endif() + # Build sentry-crash executable for native backend # Get all sources that were added to sentry target get_target_property(SENTRY_SOURCES sentry SOURCES) @@ -837,11 +858,25 @@ elseif(SENTRY_BACKEND_NATIVE) # Make sentry library depend on crash daemon so it's always built together add_dependencies(sentry sentry-crash) + if(WIN32) + add_dependencies(sentry sentry-wer) + endif() # Install daemon install(TARGETS sentry-crash RUNTIME DESTINATION bin ) + if(WIN32) + install(TARGETS sentry-wer + RUNTIME DESTINATION bin + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + ) + if(MSVC) + sentry_install(FILES $ + DESTINATION "${CMAKE_INSTALL_BINDIR}" OPTIONAL) + endif() + endif() if(DEFINED SENTRY_FOLDER) # Native backend doesn't have separate targets to organize @@ -928,6 +963,7 @@ if(SENTRY_BUILD_EXAMPLES) # to test handling SEH by-passing exceptions we need to enable the control flow guard target_compile_options(sentry_example PRIVATE $) + target_link_options(sentry_example PRIVATE $) else() # Disable all optimizations for the `sentry_example` in gcc/clang. This allows us to keep crash triggers simple. # The effects besides reproducible code-gen across compiler versions, will be negligible for build- and runtime. diff --git a/src/backends/native/minidump/sentry_minidump_windows.c b/src/backends/native/minidump/sentry_minidump_windows.c index 48c74cdf04..f70e16c465 100644 --- a/src/backends/native/minidump/sentry_minidump_windows.c +++ b/src/backends/native/minidump/sentry_minidump_windows.c @@ -69,13 +69,25 @@ sentry__write_minidump( // Prepare exception information using original pointers from crashed // process + EXCEPTION_POINTERS local_exception_pointers = { 0 }; MINIDUMP_EXCEPTION_INFORMATION exception_info = { 0 }; exception_info.ThreadId = ctx->crashed_tid; - // Use original exception pointers from crashed process's address space - exception_info.ExceptionPointers = ctx->platform.exception_pointers; - // ClientPointers=TRUE tells Windows these pointers are in the target - // process - exception_info.ClientPointers = TRUE; + if (ctx->platform.exception_pointers) { + // Use original exception pointers from crashed process's address space + exception_info.ExceptionPointers = ctx->platform.exception_pointers; + // ClientPointers=TRUE tells Windows these pointers are in the target + // process + exception_info.ClientPointers = TRUE; + } else { + // WER copies exception data into shared memory, so ClientPointers must + // be false when using the copied record/context. + local_exception_pointers.ExceptionRecord + = (PEXCEPTION_RECORD)&ctx->platform.exception_record; + local_exception_pointers.ContextRecord + = (PCONTEXT)&ctx->platform.context; + exception_info.ExceptionPointers = &local_exception_pointers; + exception_info.ClientPointers = FALSE; + } // Determine minidump type based on configuration MINIDUMP_TYPE dump_type; diff --git a/src/backends/native/sentry_wer.c b/src/backends/native/sentry_wer.c new file mode 100644 index 0000000000..7bc013ec17 --- /dev/null +++ b/src/backends/native/sentry_wer.c @@ -0,0 +1,240 @@ +#include "sentry_crash_context.h" + +#include +#include +#include +#include +#include + +#ifndef STATUS_FAIL_FAST_EXCEPTION +# define STATUS_FAIL_FAST_EXCEPTION ((DWORD)0xC0000602) +#endif + +#ifndef STATUS_STACK_BUFFER_OVERRUN +# define STATUS_STACK_BUFFER_OVERRUN ((DWORD)0xC0000409) +#endif + +typedef struct { + DWORD version; + DWORD app_pid; + uint64_t app_tid; +} sentry_native_wer_registration_t; + +static BOOL +is_fatal_wer_exception(const WER_RUNTIME_EXCEPTION_INFORMATION *info) +{ + // bIsFatal is missing in older SDKs; guard access with dwSize. + typedef struct { + DWORD dwSize; + HANDLE hProcess; + HANDLE hThread; + EXCEPTION_RECORD exceptionRecord; + CONTEXT context; + PCWSTR pwszReportId; + BOOL bIsFatal; + DWORD dwReserved; + } WER_RUNTIME_EXCEPTION_INFORMATION_19041; + + if (!info + || info->dwSize + <= offsetof(WER_RUNTIME_EXCEPTION_INFORMATION_19041, + bIsFatal)) { + return FALSE; + } + + return ((const WER_RUNTIME_EXCEPTION_INFORMATION_19041 *)info) + ->bIsFatal; +} + +static BOOL +is_native_wer_exception(DWORD code) +{ + return code == STATUS_FAIL_FAST_EXCEPTION + || code == STATUS_STACK_BUFFER_OVERRUN; +} + +static BOOL +read_registration(HANDLE process, PVOID context, + sentry_native_wer_registration_t *registration) +{ + if (!process || !context || !registration) { + return FALSE; + } + + if (!ReadProcessMemory( + process, context, registration, sizeof(*registration), NULL)) { + return FALSE; + } + + return registration->version == 1 && registration->app_pid != 0; +} + +static BOOL +open_native_crash_objects(const sentry_native_wer_registration_t *registration, + HANDLE *mapping, HANDLE *event, sentry_crash_context_t **ctx) +{ + wchar_t shm_name[SENTRY_CRASH_IPC_NAME_SIZE]; + wchar_t event_name[SENTRY_CRASH_IPC_NAME_SIZE]; + + swprintf(shm_name, SENTRY_CRASH_IPC_NAME_SIZE, L"Local\\SentryCrash-%lu-%llx", + (unsigned long)registration->app_pid, + (unsigned long long)registration->app_tid); + swprintf(event_name, SENTRY_CRASH_IPC_NAME_SIZE, + L"Local\\SentryCrashEvent-%lu-%llx", + (unsigned long)registration->app_pid, + (unsigned long long)registration->app_tid); + + *mapping = OpenFileMappingW(FILE_MAP_ALL_ACCESS, FALSE, shm_name); + if (!*mapping) { + return FALSE; + } + + *ctx = MapViewOfFile( + *mapping, FILE_MAP_ALL_ACCESS, 0, 0, SENTRY_CRASH_SHM_SIZE); + if (!*ctx) { + CloseHandle(*mapping); + *mapping = NULL; + return FALSE; + } + + if ((*ctx)->magic != SENTRY_CRASH_MAGIC) { + UnmapViewOfFile(*ctx); + CloseHandle(*mapping); + *ctx = NULL; + *mapping = NULL; + return FALSE; + } + + *event = OpenEventW(EVENT_MODIFY_STATE, FALSE, event_name); + if (!*event) { + UnmapViewOfFile(*ctx); + CloseHandle(*mapping); + *ctx = NULL; + *mapping = NULL; + return FALSE; + } + + return TRUE; +} + +static BOOL +process_wer_exception(PVOID context, + const WER_RUNTIME_EXCEPTION_INFORMATION *exception_info) +{ + if (!exception_info || !is_fatal_wer_exception(exception_info) + || !is_native_wer_exception( + exception_info->exceptionRecord.ExceptionCode)) { + return FALSE; + } + + sentry_native_wer_registration_t registration = { 0 }; + if (!read_registration( + exception_info->hProcess, context, ®istration)) { + return FALSE; + } + + HANDLE mapping = NULL; + HANDLE event = NULL; + sentry_crash_context_t *ctx = NULL; + if (!open_native_crash_objects(®istration, &mapping, &event, &ctx)) { + return FALSE; + } + + BOOL claimed = FALSE; + if (InterlockedCompareExchange(&ctx->state, SENTRY_CRASH_STATE_PROCESSING, + SENTRY_CRASH_STATE_READY) + == SENTRY_CRASH_STATE_READY) { + ctx->crashed_pid = GetProcessId(exception_info->hProcess); + ctx->crashed_tid = GetThreadId(exception_info->hThread); + ctx->platform.exception_code + = exception_info->exceptionRecord.ExceptionCode; + ctx->platform.exception_record = exception_info->exceptionRecord; + ctx->platform.context = exception_info->context; + ctx->platform.exception_pointers = NULL; + ctx->platform.num_threads = 1; + ctx->platform.threads[0].thread_id = ctx->crashed_tid; + ctx->platform.threads[0].context = exception_info->context; + + InterlockedExchange(&ctx->state, SENTRY_CRASH_STATE_CRASHED); + if (SetEvent(event)) { + DWORD wait_result = WAIT_TIMEOUT; + for (DWORD waited_ms = 0; waited_ms < 10000; waited_ms += 100) { + if (InterlockedCompareExchange( + &ctx->state, SENTRY_CRASH_STATE_DONE, + SENTRY_CRASH_STATE_DONE) + == SENTRY_CRASH_STATE_DONE) { + wait_result = WAIT_OBJECT_0; + break; + } + Sleep(100); + } + claimed = wait_result == WAIT_OBJECT_0; + if (claimed) { + TerminateProcess(exception_info->hProcess, + exception_info->exceptionRecord.ExceptionCode); + } + } + } + + CloseHandle(event); + UnmapViewOfFile(ctx); + CloseHandle(mapping); + return claimed; +} + +BOOL WINAPI +DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved) +{ + (void)instance; + (void)reason; + (void)reserved; + return TRUE; +} + +HRESULT WINAPI +OutOfProcessExceptionEventCallback(PVOID context, + const PWER_RUNTIME_EXCEPTION_INFORMATION exception_info, + BOOL *ownership_claimed, PWSTR event_name, PDWORD event_name_size, + PDWORD signature_count) +{ + (void)event_name; + (void)event_name_size; + (void)signature_count; + + *ownership_claimed = FALSE; + if (process_wer_exception(context, exception_info)) { + *ownership_claimed = TRUE; + return E_FAIL; + } + return S_OK; +} + +HRESULT WINAPI +OutOfProcessExceptionEventSignatureCallback(PVOID context, + const PWER_RUNTIME_EXCEPTION_INFORMATION exception_info, DWORD index, + PWSTR name, PDWORD name_size, PWSTR value, PDWORD value_size) +{ + (void)context; + (void)exception_info; + (void)index; + (void)name; + (void)name_size; + (void)value; + (void)value_size; + return E_FAIL; +} + +HRESULT WINAPI +OutOfProcessExceptionEventDebuggerLaunchCallback(PVOID context, + const PWER_RUNTIME_EXCEPTION_INFORMATION exception_info, + PBOOL is_custom_debugger, PWSTR debugger_launch, + PDWORD debugger_launch_size, PBOOL is_debugger_autolaunch) +{ + (void)context; + (void)exception_info; + (void)is_custom_debugger; + (void)debugger_launch; + (void)debugger_launch_size; + (void)is_debugger_autolaunch; + return E_FAIL; +} diff --git a/src/backends/native/sentry_wer.def b/src/backends/native/sentry_wer.def new file mode 100644 index 0000000000..452a14015d --- /dev/null +++ b/src/backends/native/sentry_wer.def @@ -0,0 +1,6 @@ +LIBRARY "sentry-wer.dll" + +EXPORTS + OutOfProcessExceptionEventCallback + OutOfProcessExceptionEventSignatureCallback + OutOfProcessExceptionEventDebuggerLaunchCallback diff --git a/src/backends/sentry_backend_native.c b/src/backends/sentry_backend_native.c index d9bbcf038a..9c723cb42d 100644 --- a/src/backends/sentry_backend_native.c +++ b/src/backends/sentry_backend_native.c @@ -11,6 +11,8 @@ # if defined(SENTRY_PLATFORM_LINUX) || defined(SENTRY_PLATFORM_ANDROID) # include # endif +#elif defined(SENTRY_PLATFORM_WINDOWS) +# include #endif #include @@ -29,6 +31,7 @@ #include "sentry_logs.h" #include "sentry_metrics.h" #include "sentry_options.h" +#include "sentry_os.h" #include "sentry_path.h" #include "sentry_scope.h" @@ -62,6 +65,98 @@ static sentry_mutex_t g_ipc_init_mutex = SENTRY__MUTEX_INIT; # endif #endif +#if defined(SENTRY_PLATFORM_WINDOWS) +typedef struct { + DWORD version; + DWORD app_pid; + uint64_t app_tid; +} sentry_native_wer_registration_t; + +static sentry_native_wer_registration_t g_wer_registration = { 0 }; + +static sentry_path_t *g_wer_path = NULL; + +static sentry_path_t * +wer_default_path(void) +{ + sentry_path_t *current_exe = sentry__path_current_exe(); + if (!current_exe) { + return NULL; + } + + sentry_path_t *exe_dir = sentry__path_dir(current_exe); + sentry__path_free(current_exe); + if (!exe_dir) { + return NULL; + } + + sentry_path_t *wer_path + = sentry__path_join_str(exe_dir, "sentry-wer.dll"); + sentry__path_free(exe_dir); + return wer_path; +} + +static void +wer_unregister_module(void) +{ + if (!g_wer_path) { + return; + } + + WerUnregisterRuntimeExceptionModule( + g_wer_path->path_w, &g_wer_registration); + sentry__path_free(g_wer_path); + g_wer_path = NULL; + memset(&g_wer_registration, 0, sizeof(g_wer_registration)); +} + +static void +wer_register_module(uint64_t app_tid) +{ + windows_version_t win_ver; + if (!sentry__get_windows_version(&win_ver) || win_ver.build < 19041) { + SENTRY_WARN("Native WER module not registered, because Windows " + "doesn't meet version requirements (build >= 19041)."); + return; + } + + sentry_path_t *wer_path = wer_default_path(); + if (!wer_path || !sentry__path_is_file(wer_path)) { + SENTRY_WARN("Native WER module not found"); + sentry__path_free(wer_path); + return; + } + + const DWORD one = 1; + LSTATUS reg_res = RegSetKeyValueW(HKEY_CURRENT_USER, + L"Software\\Microsoft\\Windows\\Windows Error Reporting\\" + L"RuntimeExceptionHelperModules", + wer_path->path_w, REG_DWORD, &one, sizeof(one)); + if (reg_res != ERROR_SUCCESS) { + SENTRY_WARN("registering native WER module in registry failed"); + sentry__path_free(wer_path); + return; + } + + g_wer_registration.version = 1; + g_wer_registration.app_pid = GetCurrentProcessId(); + g_wer_registration.app_tid = app_tid; + + HRESULT hr = WerRegisterRuntimeExceptionModule( + wer_path->path_w, &g_wer_registration); + if (FAILED(hr)) { + SENTRY_WARN("registering native WER module failed"); + sentry__path_free(wer_path); + memset(&g_wer_registration, 0, sizeof(g_wer_registration)); + return; + } + + SENTRY_DEBUGF("registered native WER module \"%s\"", wer_path->path); + g_wer_path = wer_path; +} + +#endif + /** * Native backend state */ @@ -412,6 +507,10 @@ native_backend_startup( SENTRY_DEBUG("Daemon signaled ready"); } +# if defined(SENTRY_PLATFORM_WINDOWS) + wer_register_module(tid); +# endif + if (sentry__crash_handler_init(state->ipc) < 0) { SENTRY_WARN("failed to initialize crash handler"); # if defined(SENTRY_PLATFORM_UNIX) @@ -446,6 +545,10 @@ native_backend_shutdown(sentry_backend_t *backend) return; } +#if defined(SENTRY_PLATFORM_WINDOWS) + wer_unregister_module(); +#endif + // Shutdown crash handlers (signal handlers on Linux/macOS, Mach exception // handler on iOS) sentry__crash_handler_shutdown(); diff --git a/tests/cmake.py b/tests/cmake.py index 9d043b10bd..350e4570bb 100644 --- a/tests/cmake.py +++ b/tests/cmake.py @@ -271,6 +271,7 @@ def cmake_build(cwd, targets, options): from tests.win_utils import check_binary_version check_binary_version(Path(cwd) / "sentry.dll") + check_binary_version(Path(cwd) / "sentry-wer.dll") check_binary_version(Path(cwd) / "crashpad_wer.dll") check_binary_version(Path(cwd) / "crashpad_handler.exe") diff --git a/tests/conftest.py b/tests/conftest.py index 6582737c2d..56e27eaff7 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -104,9 +104,9 @@ def _record_test_start(): def pytest_addoption(parser): parser.addoption( - "--with_crashpad_wer", + "--with_wer", action="store_true", - help="Enables tests for the crashpad WER module on Windows", + help="Enables tests for the native and crashpad WER modules on Windows", ) parser.addoption( "--benchmark_out", @@ -116,16 +116,14 @@ def pytest_addoption(parser): def pytest_runtest_setup(item): - if "with_crashpad_wer" in item.keywords and not item.config.getoption( - "--with_crashpad_wer" - ): - pytest.skip("need --with_crashpad_wer to run this test") + if "with_wer" in item.keywords and not item.config.getoption("--with_wer"): + pytest.skip("need --with_wer to run this test") def pytest_configure(config): config.addinivalue_line( "markers", - "with_crashpad_wer: mark test to only run when WER testing is enabled", + "with_wer: mark test to only run when WER testing is enabled", ) diff --git a/tests/test_integration_crashpad.py b/tests/test_integration_crashpad.py index c91724ee65..871b3e3232 100644 --- a/tests/test_integration_crashpad.py +++ b/tests/test_integration_crashpad.py @@ -285,8 +285,8 @@ def wait_for_no_werfault(timeout=30.0, poll_interval=0.5): reason="Test covers Windows-specific crashes which can only be covered via the Crashpad WER module", ) # this test currently can't run on CI because the Windows-image doesn't properly support WER, if you want to run the -# test locally, invoke pytest with the --with_crashpad_wer option which is matched with this marker in the runtest setup -@pytest.mark.with_crashpad_wer +# test locally, invoke pytest with the --with_wer option which is matched with this marker in the runtest setup +@pytest.mark.with_wer @pytest.mark.parametrize( "run_args", [ @@ -803,8 +803,8 @@ def test_crashpad_external_crash_reporter(cmake, httpserver, run_args): reason="Test covers Windows-specific crashes which can only be covered via the Crashpad WER module", ) # this test currently can't run on CI because the Windows-image doesn't properly support WER, if you want to run the -# test locally, invoke pytest with the --with_crashpad_wer option which is matched with this marker in the runtest setup -@pytest.mark.with_crashpad_wer +# test locally, invoke pytest with the --with_wer option which is matched with this marker in the runtest setup +@pytest.mark.with_wer @pytest.mark.parametrize( "run_args", [ diff --git a/tests/test_integration_native.py b/tests/test_integration_native.py index 0267d294c1..3f9b105efe 100644 --- a/tests/test_integration_native.py +++ b/tests/test_integration_native.py @@ -87,6 +87,41 @@ def test_native_capture_crash(cmake, httpserver): assert waiting.result +@pytest.mark.skipif( + sys.platform != "win32" or os.environ.get("TEST_MINGW"), + reason="WER crash tests are only available in MSVC Windows builds", +) +@pytest.mark.with_wer +@pytest.mark.parametrize("crash_arg", ["fastfail", "stack-buffer-overrun"]) +def test_native_wer(cmake, httpserver, crash_arg): + """Test WER crash capture with native backend""" + tmp_path = cmake(["sentry_example"], {"SENTRY_BACKEND": "native"}) + + httpserver.expect_oneshot_request("/api/123456/envelope/").respond_with_data("OK") + + with httpserver.wait(timeout=10) as waiting: + run_crash( + tmp_path, + "sentry_example", + ["log", "stdout", crash_arg], + env=dict(os.environ, SENTRY_DSN=make_dsn(httpserver)), + ) + assert waiting.result + + assert len(httpserver.log) >= 1 + envelope = Envelope.deserialize(httpserver.log[0][0].get_data()) + event = envelope.get_event() + assert event is not None + assert event["level"] == "fatal" + + exc = event["exception"]["values"][0] + assert exc["mechanism"]["type"] == "signalhandler" + assert exc["mechanism"]["handled"] is False + assert exc["mechanism"]["meta"]["signal"]["number"] == 0xC0000409 + assert "stacktrace" in exc + assert len(exc["stacktrace"]["frames"]) > 0 + + @pytest.mark.skipif(not has_oom, reason="OOM test unreliable in this environment") def test_native_oom(cmake, httpserver): """Test OOM crash capture with native backend""" diff --git a/tests/test_integration_screenshot.py b/tests/test_integration_screenshot.py index ad6c5472cd..450ae006ed 100644 --- a/tests/test_integration_screenshot.py +++ b/tests/test_integration_screenshot.py @@ -125,8 +125,8 @@ def test_capture_screenshot_crashpad(cmake, httpserver, run_args): reason="Screenshots are only supported on Windows", ) # this test currently can't run on CI because the Windows-image doesn't properly support WER, if you want to run the -# test locally, invoke pytest with the --with_crashpad_wer option which is matched with this marker in the runtest setup -@pytest.mark.with_crashpad_wer +# test locally, invoke pytest with the --with_wer option which is matched with this marker in the runtest setup +@pytest.mark.with_wer @pytest.mark.parametrize( "run_args", [ From 539d5e22b3a002de3de28ba092fa3c9449251072 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Fri, 8 May 2026 18:05:51 +0200 Subject: [PATCH 02/18] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 34edd2286a..e3de4d2a6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ **Fixes**: -- Native/Windows: capture fast-fail and stack buffer overrun crashes via WER. +- Native/Windows: capture fast-fail and stack buffer overrun crashes via WER. ([#1710](https://github.com/getsentry/sentry-native/pull/1710)) ## 0.14.0 From 21adffa8a96ec8136ce05066ec9b0c621e341b85 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Fri, 8 May 2026 18:10:38 +0200 Subject: [PATCH 03/18] fix(native): keep WER callback ownership claim --- src/backends/native/sentry_wer.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/backends/native/sentry_wer.c b/src/backends/native/sentry_wer.c index 7bc013ec17..096b177f05 100644 --- a/src/backends/native/sentry_wer.c +++ b/src/backends/native/sentry_wer.c @@ -204,7 +204,6 @@ OutOfProcessExceptionEventCallback(PVOID context, *ownership_claimed = FALSE; if (process_wer_exception(context, exception_info)) { *ownership_claimed = TRUE; - return E_FAIL; } return S_OK; } From 7ba3742e7d3c5bc4d035188bbd886b935faa6fb1 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Fri, 8 May 2026 18:14:39 +0200 Subject: [PATCH 04/18] Fix style --- src/backends/native/sentry_wer.c | 24 ++++++++++-------------- src/backends/sentry_backend_native.c | 3 +-- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/src/backends/native/sentry_wer.c b/src/backends/native/sentry_wer.c index 096b177f05..3123e4b637 100644 --- a/src/backends/native/sentry_wer.c +++ b/src/backends/native/sentry_wer.c @@ -3,8 +3,8 @@ #include #include #include -#include #include +#include #ifndef STATUS_FAIL_FAST_EXCEPTION # define STATUS_FAIL_FAST_EXCEPTION ((DWORD)0xC0000602) @@ -37,13 +37,11 @@ is_fatal_wer_exception(const WER_RUNTIME_EXCEPTION_INFORMATION *info) if (!info || info->dwSize - <= offsetof(WER_RUNTIME_EXCEPTION_INFORMATION_19041, - bIsFatal)) { + <= offsetof(WER_RUNTIME_EXCEPTION_INFORMATION_19041, bIsFatal)) { return FALSE; } - return ((const WER_RUNTIME_EXCEPTION_INFORMATION_19041 *)info) - ->bIsFatal; + return ((const WER_RUNTIME_EXCEPTION_INFORMATION_19041 *)info)->bIsFatal; } static BOOL @@ -76,8 +74,8 @@ open_native_crash_objects(const sentry_native_wer_registration_t *registration, wchar_t shm_name[SENTRY_CRASH_IPC_NAME_SIZE]; wchar_t event_name[SENTRY_CRASH_IPC_NAME_SIZE]; - swprintf(shm_name, SENTRY_CRASH_IPC_NAME_SIZE, L"Local\\SentryCrash-%lu-%llx", - (unsigned long)registration->app_pid, + swprintf(shm_name, SENTRY_CRASH_IPC_NAME_SIZE, + L"Local\\SentryCrash-%lu-%llx", (unsigned long)registration->app_pid, (unsigned long long)registration->app_tid); swprintf(event_name, SENTRY_CRASH_IPC_NAME_SIZE, L"Local\\SentryCrashEvent-%lu-%llx", @@ -118,8 +116,8 @@ open_native_crash_objects(const sentry_native_wer_registration_t *registration, } static BOOL -process_wer_exception(PVOID context, - const WER_RUNTIME_EXCEPTION_INFORMATION *exception_info) +process_wer_exception( + PVOID context, const WER_RUNTIME_EXCEPTION_INFORMATION *exception_info) { if (!exception_info || !is_fatal_wer_exception(exception_info) || !is_native_wer_exception( @@ -128,8 +126,7 @@ process_wer_exception(PVOID context, } sentry_native_wer_registration_t registration = { 0 }; - if (!read_registration( - exception_info->hProcess, context, ®istration)) { + if (!read_registration(exception_info->hProcess, context, ®istration)) { return FALSE; } @@ -159,9 +156,8 @@ process_wer_exception(PVOID context, if (SetEvent(event)) { DWORD wait_result = WAIT_TIMEOUT; for (DWORD waited_ms = 0; waited_ms < 10000; waited_ms += 100) { - if (InterlockedCompareExchange( - &ctx->state, SENTRY_CRASH_STATE_DONE, - SENTRY_CRASH_STATE_DONE) + if (InterlockedCompareExchange(&ctx->state, + SENTRY_CRASH_STATE_DONE, SENTRY_CRASH_STATE_DONE) == SENTRY_CRASH_STATE_DONE) { wait_result = WAIT_OBJECT_0; break; diff --git a/src/backends/sentry_backend_native.c b/src/backends/sentry_backend_native.c index 9c723cb42d..f287820f01 100644 --- a/src/backends/sentry_backend_native.c +++ b/src/backends/sentry_backend_native.c @@ -90,8 +90,7 @@ wer_default_path(void) return NULL; } - sentry_path_t *wer_path - = sentry__path_join_str(exe_dir, "sentry-wer.dll"); + sentry_path_t *wer_path = sentry__path_join_str(exe_dir, "sentry-wer.dll"); sentry__path_free(exe_dir); return wer_path; } From baa9b0d5c5a8500b880a569a9aca5d632c93536e Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Fri, 8 May 2026 18:15:44 +0200 Subject: [PATCH 05/18] sentry_add_version_resource --- CMakeLists.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9655218688..12c2ba0097 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -783,9 +783,7 @@ elseif(SENTRY_BACKEND_NATIVE) if(SENTRY_BUILD_RUNTIMESTATIC AND MSVC) set_property(TARGET sentry-wer PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") endif() - if(SENTRY_BUILD_SHARED_LIBS) - sentry_add_version_resource(sentry-wer "Native WER Module") - endif() + sentry_add_version_resource(sentry-wer "Native WER Module") endif() # Build sentry-crash executable for native backend From 00ad41e039d1ec84db9e4525128a99c5645abdbb Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Fri, 8 May 2026 18:16:55 +0200 Subject: [PATCH 06/18] sentry_native_wer_registration_t --- src/backends/native/sentry_crash_context.h | 6 ++++++ src/backends/native/sentry_wer.c | 6 ------ src/backends/sentry_backend_native.c | 6 ------ 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/backends/native/sentry_crash_context.h b/src/backends/native/sentry_crash_context.h index da386da9a7..c3a53666c6 100644 --- a/src/backends/native/sentry_crash_context.h +++ b/src/backends/native/sentry_crash_context.h @@ -242,6 +242,12 @@ typedef struct { sentry_thread_context_windows_t threads[SENTRY_CRASH_MAX_THREADS]; } sentry_crash_platform_windows_t; +typedef struct { + DWORD version; + DWORD app_pid; + uint64_t app_tid; +} sentry_native_wer_registration_t; + # ifdef _MSC_VER # pragma warning(pop) # endif diff --git a/src/backends/native/sentry_wer.c b/src/backends/native/sentry_wer.c index 3123e4b637..b9b4f2204a 100644 --- a/src/backends/native/sentry_wer.c +++ b/src/backends/native/sentry_wer.c @@ -14,12 +14,6 @@ # define STATUS_STACK_BUFFER_OVERRUN ((DWORD)0xC0000409) #endif -typedef struct { - DWORD version; - DWORD app_pid; - uint64_t app_tid; -} sentry_native_wer_registration_t; - static BOOL is_fatal_wer_exception(const WER_RUNTIME_EXCEPTION_INFORMATION *info) { diff --git a/src/backends/sentry_backend_native.c b/src/backends/sentry_backend_native.c index f287820f01..335a3b45e6 100644 --- a/src/backends/sentry_backend_native.c +++ b/src/backends/sentry_backend_native.c @@ -66,12 +66,6 @@ static sentry_mutex_t g_ipc_init_mutex = SENTRY__MUTEX_INIT; #endif #if defined(SENTRY_PLATFORM_WINDOWS) -typedef struct { - DWORD version; - DWORD app_pid; - uint64_t app_tid; -} sentry_native_wer_registration_t; - static sentry_native_wer_registration_t g_wer_registration = { 0 }; static sentry_path_t *g_wer_path = NULL; From 5ba07276d183625ca0f13f75a489a2152f3ae902 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Fri, 8 May 2026 18:18:26 +0200 Subject: [PATCH 07/18] ExceptionRecord --- src/backends/native/minidump/sentry_minidump_windows.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/backends/native/minidump/sentry_minidump_windows.c b/src/backends/native/minidump/sentry_minidump_windows.c index f70e16c465..b56de6af9f 100644 --- a/src/backends/native/minidump/sentry_minidump_windows.c +++ b/src/backends/native/minidump/sentry_minidump_windows.c @@ -70,6 +70,7 @@ sentry__write_minidump( // Prepare exception information using original pointers from crashed // process EXCEPTION_POINTERS local_exception_pointers = { 0 }; + EXCEPTION_RECORD local_exception_record = { 0 }; MINIDUMP_EXCEPTION_INFORMATION exception_info = { 0 }; exception_info.ThreadId = ctx->crashed_tid; if (ctx->platform.exception_pointers) { @@ -81,8 +82,9 @@ sentry__write_minidump( } else { // WER copies exception data into shared memory, so ClientPointers must // be false when using the copied record/context. - local_exception_pointers.ExceptionRecord - = (PEXCEPTION_RECORD)&ctx->platform.exception_record; + local_exception_record = ctx->platform.exception_record; + local_exception_record.ExceptionRecord = NULL; + local_exception_pointers.ExceptionRecord = &local_exception_record; local_exception_pointers.ContextRecord = (PCONTEXT)&ctx->platform.context; exception_info.ExceptionPointers = &local_exception_pointers; From 3895c77953ed70aed86a932104d97d442d0b553b Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Fri, 8 May 2026 18:26:29 +0200 Subject: [PATCH 08/18] fix(native): clean up WER registry value --- src/backends/sentry_backend_native.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/backends/sentry_backend_native.c b/src/backends/sentry_backend_native.c index 335a3b45e6..8d341e0b7f 100644 --- a/src/backends/sentry_backend_native.c +++ b/src/backends/sentry_backend_native.c @@ -98,6 +98,10 @@ wer_unregister_module(void) WerUnregisterRuntimeExceptionModule( g_wer_path->path_w, &g_wer_registration); + RegDeleteKeyValueW(HKEY_CURRENT_USER, + L"Software\\Microsoft\\Windows\\Windows Error Reporting\\" + L"RuntimeExceptionHelperModules", + g_wer_path->path_w); sentry__path_free(g_wer_path); g_wer_path = NULL; memset(&g_wer_registration, 0, sizeof(g_wer_registration)); From f0817a00afcf8b3a30421eb34e3593947743f885 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Fri, 8 May 2026 18:34:32 +0200 Subject: [PATCH 09/18] fix(native): clean up failed WER registration --- src/backends/sentry_backend_native.c | 29 ++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/backends/sentry_backend_native.c b/src/backends/sentry_backend_native.c index 8d341e0b7f..505d8f3d1e 100644 --- a/src/backends/sentry_backend_native.c +++ b/src/backends/sentry_backend_native.c @@ -70,6 +70,24 @@ static sentry_native_wer_registration_t g_wer_registration = { 0 }; static sentry_path_t *g_wer_path = NULL; +static LSTATUS +wer_set_registry_value(const sentry_path_t *wer_path, DWORD value) +{ + return RegSetKeyValueW(HKEY_CURRENT_USER, + L"Software\\Microsoft\\Windows\\Windows Error Reporting\\" + L"RuntimeExceptionHelperModules", + wer_path->path_w, REG_DWORD, &value, sizeof(value)); +} + +static LSTATUS +wer_delete_registry_value(const sentry_path_t *wer_path) +{ + return RegDeleteKeyValueW(HKEY_CURRENT_USER, + L"Software\\Microsoft\\Windows\\Windows Error Reporting\\" + L"RuntimeExceptionHelperModules", + wer_path->path_w); +} + static sentry_path_t * wer_default_path(void) { @@ -98,10 +116,7 @@ wer_unregister_module(void) WerUnregisterRuntimeExceptionModule( g_wer_path->path_w, &g_wer_registration); - RegDeleteKeyValueW(HKEY_CURRENT_USER, - L"Software\\Microsoft\\Windows\\Windows Error Reporting\\" - L"RuntimeExceptionHelperModules", - g_wer_path->path_w); + wer_delete_registry_value(g_wer_path); sentry__path_free(g_wer_path); g_wer_path = NULL; memset(&g_wer_registration, 0, sizeof(g_wer_registration)); @@ -125,10 +140,7 @@ wer_register_module(uint64_t app_tid) } const DWORD one = 1; - LSTATUS reg_res = RegSetKeyValueW(HKEY_CURRENT_USER, - L"Software\\Microsoft\\Windows\\Windows Error Reporting\\" - L"RuntimeExceptionHelperModules", - wer_path->path_w, REG_DWORD, &one, sizeof(one)); + LSTATUS reg_res = wer_set_registry_value(wer_path, one); if (reg_res != ERROR_SUCCESS) { SENTRY_WARN("registering native WER module in registry failed"); sentry__path_free(wer_path); @@ -143,6 +155,7 @@ wer_register_module(uint64_t app_tid) wer_path->path_w, &g_wer_registration); if (FAILED(hr)) { SENTRY_WARN("registering native WER module failed"); + wer_delete_registry_value(wer_path); sentry__path_free(wer_path); memset(&g_wer_registration, 0, sizeof(g_wer_registration)); return; From 238041d698cb52eafab27f7c340ac7deb77b00d4 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Fri, 8 May 2026 18:40:37 +0200 Subject: [PATCH 10/18] sentry_wer_registration_t --- src/backends/native/sentry_crash_context.h | 2 +- src/backends/native/sentry_wer.c | 8 ++++---- src/backends/sentry_backend_native.c | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/backends/native/sentry_crash_context.h b/src/backends/native/sentry_crash_context.h index c3a53666c6..01c3ebbe04 100644 --- a/src/backends/native/sentry_crash_context.h +++ b/src/backends/native/sentry_crash_context.h @@ -246,7 +246,7 @@ typedef struct { DWORD version; DWORD app_pid; uint64_t app_tid; -} sentry_native_wer_registration_t; +} sentry_wer_registration_t; # ifdef _MSC_VER # pragma warning(pop) diff --git a/src/backends/native/sentry_wer.c b/src/backends/native/sentry_wer.c index b9b4f2204a..674b0496ae 100644 --- a/src/backends/native/sentry_wer.c +++ b/src/backends/native/sentry_wer.c @@ -46,8 +46,8 @@ is_native_wer_exception(DWORD code) } static BOOL -read_registration(HANDLE process, PVOID context, - sentry_native_wer_registration_t *registration) +read_registration( + HANDLE process, PVOID context, sentry_wer_registration_t *registration) { if (!process || !context || !registration) { return FALSE; @@ -62,7 +62,7 @@ read_registration(HANDLE process, PVOID context, } static BOOL -open_native_crash_objects(const sentry_native_wer_registration_t *registration, +open_native_crash_objects(const sentry_wer_registration_t *registration, HANDLE *mapping, HANDLE *event, sentry_crash_context_t **ctx) { wchar_t shm_name[SENTRY_CRASH_IPC_NAME_SIZE]; @@ -119,7 +119,7 @@ process_wer_exception( return FALSE; } - sentry_native_wer_registration_t registration = { 0 }; + sentry_wer_registration_t registration = { 0 }; if (!read_registration(exception_info->hProcess, context, ®istration)) { return FALSE; } diff --git a/src/backends/sentry_backend_native.c b/src/backends/sentry_backend_native.c index 505d8f3d1e..73828e033d 100644 --- a/src/backends/sentry_backend_native.c +++ b/src/backends/sentry_backend_native.c @@ -66,7 +66,7 @@ static sentry_mutex_t g_ipc_init_mutex = SENTRY__MUTEX_INIT; #endif #if defined(SENTRY_PLATFORM_WINDOWS) -static sentry_native_wer_registration_t g_wer_registration = { 0 }; +static sentry_wer_registration_t g_wer_registration = { 0 }; static sentry_path_t *g_wer_path = NULL; From 7fed4aab3fedc1898f4bcdea6f50396bbddecfb7 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Fri, 8 May 2026 18:41:46 +0200 Subject: [PATCH 11/18] unregister --- src/backends/sentry_backend_native.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/backends/sentry_backend_native.c b/src/backends/sentry_backend_native.c index 73828e033d..b2141fb2eb 100644 --- a/src/backends/sentry_backend_native.c +++ b/src/backends/sentry_backend_native.c @@ -526,6 +526,7 @@ native_backend_startup( # if defined(SENTRY_PLATFORM_UNIX) kill(state->daemon_pid, SIGTERM); # elif defined(SENTRY_PLATFORM_WINDOWS) + wer_unregister_module(); // On Windows, terminate the daemon process HANDLE hDaemon = OpenProcess(PROCESS_TERMINATE, FALSE, state->daemon_pid); From 1581cab76e9b243a47d726b393359c5f5c0bf35e Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Fri, 8 May 2026 18:47:39 +0200 Subject: [PATCH 12/18] assert_native_crash --- tests/assertions.py | 14 ++++++++++++++ tests/test_integration_native.py | 12 ++---------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/tests/assertions.py b/tests/assertions.py index 7710b25e0a..ba77b400d4 100644 --- a/tests/assertions.py +++ b/tests/assertions.py @@ -423,6 +423,20 @@ def assert_inproc_crash(envelope): assert_stacktrace(envelope, inside_exception=True, check_size=False) +def assert_native_crash(envelope, exception_code=None): + event = envelope.get_event() + assert event is not None + assert_matches(event, {"level": "fatal"}) + + exc = event["exception"]["values"][0] + assert exc["mechanism"]["type"] == "signalhandler" + assert exc["mechanism"]["handled"] is False + if exception_code is not None: + assert exc["mechanism"]["meta"]["signal"]["number"] == exception_code + assert "stacktrace" in exc + assert len(exc["stacktrace"]["frames"]) > 0 + + def assert_crash_timestamp(has_files, tmp_path): # The crash file should survive a `sentry_init` and should still be there # even after restarts. diff --git a/tests/test_integration_native.py b/tests/test_integration_native.py index 3f9b105efe..150e6af922 100644 --- a/tests/test_integration_native.py +++ b/tests/test_integration_native.py @@ -21,6 +21,7 @@ from .assertions import ( assert_breadcrumb, assert_meta, + assert_native_crash, assert_session, wait_for_file, assert_user_feedback, @@ -110,16 +111,7 @@ def test_native_wer(cmake, httpserver, crash_arg): assert len(httpserver.log) >= 1 envelope = Envelope.deserialize(httpserver.log[0][0].get_data()) - event = envelope.get_event() - assert event is not None - assert event["level"] == "fatal" - - exc = event["exception"]["values"][0] - assert exc["mechanism"]["type"] == "signalhandler" - assert exc["mechanism"]["handled"] is False - assert exc["mechanism"]["meta"]["signal"]["number"] == 0xC0000409 - assert "stacktrace" in exc - assert len(exc["stacktrace"]["frames"]) > 0 + assert_native_crash(envelope, exception_code=0xC0000409) @pytest.mark.skipif(not has_oom, reason="OOM test unreliable in this environment") From 91c70bd838678704466455353c854f1edc2380d0 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Fri, 8 May 2026 18:49:41 +0200 Subject: [PATCH 13/18] timeout --- src/backends/native/sentry_wer.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/backends/native/sentry_wer.c b/src/backends/native/sentry_wer.c index 674b0496ae..9a397484ac 100644 --- a/src/backends/native/sentry_wer.c +++ b/src/backends/native/sentry_wer.c @@ -149,14 +149,18 @@ process_wer_exception( InterlockedExchange(&ctx->state, SENTRY_CRASH_STATE_CRASHED); if (SetEvent(event)) { DWORD wait_result = WAIT_TIMEOUT; - for (DWORD waited_ms = 0; waited_ms < 10000; waited_ms += 100) { + uint64_t timeout_ms = ctx->shutdown_timeout + ? ctx->shutdown_timeout + : SENTRY_CRASH_HANDLER_WAIT_TIMEOUT_MS; + for (uint64_t waited_ms = 0; waited_ms < timeout_ms; + waited_ms += SENTRY_CRASH_HANDLER_POLL_INTERVAL_MS) { if (InterlockedCompareExchange(&ctx->state, SENTRY_CRASH_STATE_DONE, SENTRY_CRASH_STATE_DONE) == SENTRY_CRASH_STATE_DONE) { wait_result = WAIT_OBJECT_0; break; } - Sleep(100); + Sleep(SENTRY_CRASH_HANDLER_POLL_INTERVAL_MS); } claimed = wait_result == WAIT_OBJECT_0; if (claimed) { From e13e1efe56476cf6fcaa35f34ebfbe585fb7def7 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Fri, 8 May 2026 18:51:36 +0200 Subject: [PATCH 14/18] fix(native): claim WER crashes after signaling daemon --- src/backends/native/sentry_wer.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/backends/native/sentry_wer.c b/src/backends/native/sentry_wer.c index 9a397484ac..30d76d8ce9 100644 --- a/src/backends/native/sentry_wer.c +++ b/src/backends/native/sentry_wer.c @@ -148,7 +148,8 @@ process_wer_exception( InterlockedExchange(&ctx->state, SENTRY_CRASH_STATE_CRASHED); if (SetEvent(event)) { - DWORD wait_result = WAIT_TIMEOUT; + claimed = TRUE; + BOOL processed = FALSE; uint64_t timeout_ms = ctx->shutdown_timeout ? ctx->shutdown_timeout : SENTRY_CRASH_HANDLER_WAIT_TIMEOUT_MS; @@ -157,13 +158,12 @@ process_wer_exception( if (InterlockedCompareExchange(&ctx->state, SENTRY_CRASH_STATE_DONE, SENTRY_CRASH_STATE_DONE) == SENTRY_CRASH_STATE_DONE) { - wait_result = WAIT_OBJECT_0; + processed = TRUE; break; } Sleep(SENTRY_CRASH_HANDLER_POLL_INTERVAL_MS); } - claimed = wait_result == WAIT_OBJECT_0; - if (claimed) { + if (processed) { TerminateProcess(exception_info->hProcess, exception_info->exceptionRecord.ExceptionCode); } From 2685faae126558238360aeccb5ac6d328873455d Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Fri, 8 May 2026 18:57:53 +0200 Subject: [PATCH 15/18] fix(native): terminate claimed WER crashes --- src/backends/native/sentry_wer.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/backends/native/sentry_wer.c b/src/backends/native/sentry_wer.c index 30d76d8ce9..85a0b23d19 100644 --- a/src/backends/native/sentry_wer.c +++ b/src/backends/native/sentry_wer.c @@ -149,7 +149,6 @@ process_wer_exception( InterlockedExchange(&ctx->state, SENTRY_CRASH_STATE_CRASHED); if (SetEvent(event)) { claimed = TRUE; - BOOL processed = FALSE; uint64_t timeout_ms = ctx->shutdown_timeout ? ctx->shutdown_timeout : SENTRY_CRASH_HANDLER_WAIT_TIMEOUT_MS; @@ -158,15 +157,12 @@ process_wer_exception( if (InterlockedCompareExchange(&ctx->state, SENTRY_CRASH_STATE_DONE, SENTRY_CRASH_STATE_DONE) == SENTRY_CRASH_STATE_DONE) { - processed = TRUE; break; } Sleep(SENTRY_CRASH_HANDLER_POLL_INTERVAL_MS); } - if (processed) { - TerminateProcess(exception_info->hProcess, - exception_info->exceptionRecord.ExceptionCode); - } + TerminateProcess(exception_info->hProcess, + exception_info->exceptionRecord.ExceptionCode); } } From c40d991c6e270214b37307b8e3a62578b16da2b1 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Fri, 8 May 2026 19:46:20 +0200 Subject: [PATCH 16/18] skipif --- tests/test_integration_native.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_integration_native.py b/tests/test_integration_native.py index 150e6af922..86389c9287 100644 --- a/tests/test_integration_native.py +++ b/tests/test_integration_native.py @@ -89,7 +89,7 @@ def test_native_capture_crash(cmake, httpserver): @pytest.mark.skipif( - sys.platform != "win32" or os.environ.get("TEST_MINGW"), + sys.platform != "win32" or bool(os.environ.get("TEST_MINGW")), reason="WER crash tests are only available in MSVC Windows builds", ) @pytest.mark.with_wer From 0bca2b91efd5bc60c3843ef573e35aa8bf69e5f6 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Fri, 8 May 2026 19:55:34 +0200 Subject: [PATCH 17/18] script --- scripts/run_tests.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/run_tests.ps1 b/scripts/run_tests.ps1 index a923e6e084..c505f0e736 100644 --- a/scripts/run_tests.ps1 +++ b/scripts/run_tests.ps1 @@ -13,7 +13,7 @@ param ( [string]$Keyword = "", ## Defines the number of parallel runners via pytest-xdist. This is highly experimental since tests remove database paths. (default: 1) [int]$Parallelism = 1, - ## Disables tests that require the crashpad WER module. (default: false) + ## Disables tests that require WER modules. (default: false) [switch]$WithoutCrashpadWer = $false, ## Disables stdout/stderr capture through pytest (default: false) [switch]$DisableCapture = $false, @@ -46,7 +46,7 @@ if ($Parallelism -gt 1) if (-not $WithoutCrashpadWer -and -not $Unit) { - $pytestCommand += " --with_crashpad_wer" + $pytestCommand += " --with_wer" } if ($Keyword) From d33006420dd6ee0e805fadaf9f562a8d38ff934a Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Fri, 8 May 2026 21:10:04 +0200 Subject: [PATCH 18/18] WithoutCrashpadWer --- scripts/run_tests.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/run_tests.ps1 b/scripts/run_tests.ps1 index c505f0e736..8dfaa46f85 100644 --- a/scripts/run_tests.ps1 +++ b/scripts/run_tests.ps1 @@ -14,7 +14,7 @@ param ( ## Defines the number of parallel runners via pytest-xdist. This is highly experimental since tests remove database paths. (default: 1) [int]$Parallelism = 1, ## Disables tests that require WER modules. (default: false) - [switch]$WithoutCrashpadWer = $false, + [switch]$WithoutWer = $false, ## Disables stdout/stderr capture through pytest (default: false) [switch]$DisableCapture = $false, ## Defines the maximum number of failing tests before the test session is stopped. 0 means infinite. Will not do what you expect, together with Parallelism > 1 (default: 0) @@ -44,7 +44,7 @@ if ($Parallelism -gt 1) $pytestCommand += " -n $Parallelism" } -if (-not $WithoutCrashpadWer -and -not $Unit) +if (-not $WithoutWer -and -not $Unit) { $pytestCommand += " --with_wer" }