From 1c53bf6b2ac3964633031f1618f5dc16e74e3883 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Thu, 2 Jul 2026 14:13:54 +0200 Subject: [PATCH 1/7] feat: add WER integration Adds sentry_integration_wer_new() which creates an integration that uses a scope observer to sync sentry scope state to Windows Error Reporting: - Tags are synced via WerRegisterCustomMetadata (loaded dynamically for compatibility with pre-19H1 Windows 10 builds). - File attachments are registered with WerRegisterFile. - Buffer attachments are registered with WerRegisterMemoryBlock. When tags or attachments are removed via the sentry API, the corresponding WER registrations are cleaned up. --- CHANGELOG.md | 4 + CMakeLists.txt | 8 + src/CMakeLists.txt | 7 + src/integrations/sentry_integration_wer.c | 253 ++++++++++++++++++++++ src/integrations/sentry_integration_wer.h | 11 + src/sentry_options.c | 7 + tests/test_integration_wer.py | 187 ++++++++++++++++ 7 files changed, 477 insertions(+) create mode 100644 src/integrations/sentry_integration_wer.c create mode 100644 src/integrations/sentry_integration_wer.h create mode 100644 tests/test_integration_wer.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 3494f6f76..5c1ce8f57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +**Features** + +- Windows: add WER integration for syncing tags and attachments to WER. ([#1837](https://github.com/getsentry/sentry-native/pull/1837)) + **Fixes**: - Crashpad: wait reliably for crash report uploads. ([#1885](https://github.com/getsentry/sentry-native/pull/1885)) diff --git a/CMakeLists.txt b/CMakeLists.txt index e26c4163f..5afd2a5b4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -963,6 +963,14 @@ if(SENTRY_INTEGRATION_QT) target_link_libraries(sentry PRIVATE Qt${Qt_VERSION_MAJOR}::Core) endif() +option(SENTRY_INTEGRATION_WER "Build WER (Windows Error Reporting) integration") +if(SENTRY_INTEGRATION_WER AND WIN32 + AND (SENTRY_BACKEND_BREAKPAD OR SENTRY_BACKEND_CRASHPAD)) + message(WARNING + "SENTRY_BACKEND=${SENTRY_BACKEND} is not compatible with WER. " + "Use SENTRY_BACKEND=none, inproc, or native with SENTRY_INTEGRATION_WER.") +endif() + include(CMakePackageConfigHelpers) configure_package_config_file(sentry-config.cmake.in sentry-config.cmake INSTALL_DESTINATION "${CMAKE_INSTALL_CMAKEDIR}") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0a2f48839..bf37dd18a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -307,6 +307,13 @@ if(SENTRY_INTEGRATION_QT) integrations/sentry_integration_qt.h ) endif() +if(SENTRY_INTEGRATION_WER AND WIN32) + target_compile_definitions(sentry PRIVATE SENTRY_INTEGRATION_WER) + sentry_target_sources_cwd(sentry + integrations/sentry_integration_wer.c + integrations/sentry_integration_wer.h + ) +endif() # screenshot if(SENTRY_SCREENSHOT_WINDOWS) diff --git a/src/integrations/sentry_integration_wer.c b/src/integrations/sentry_integration_wer.c new file mode 100644 index 000000000..da968f0d2 --- /dev/null +++ b/src/integrations/sentry_integration_wer.c @@ -0,0 +1,253 @@ +#include "sentry_integration_wer.h" + +#include "sentry_alloc.h" +#include "sentry_attachment.h" +#include "sentry_core.h" +#include "sentry_logger.h" +#include "sentry_scope.h" +#include "sentry_string.h" + +#include +#include + +// Windows 8+ SDK +#ifndef WER_FILE_ANONYMOUS_DATA +# define WER_FILE_ANONYMOUS_DATA 0x2 +#endif + +typedef struct sentry_integration_wer_data_s { + HRESULT(WINAPI *WerRegisterCustomMetadata)(PCWSTR, PCWSTR); + HRESULT(WINAPI *WerUnregisterCustomMetadata)(PCWSTR); + sentry_scope_observer_t *observer; +} sentry_integration_wer_data_t; + +static void +wer_init(sentry_integration_wer_data_t *wer_data) +{ + if (wer_data->WerRegisterCustomMetadata) { + return; + } + HMODULE kernel32 = GetModuleHandleW(L"kernel32.dll"); + if (kernel32) { + wer_data->WerRegisterCustomMetadata = (HRESULT(WINAPI *)(PCWSTR, + PCWSTR))GetProcAddress(kernel32, "WerRegisterCustomMetadata"); + wer_data->WerUnregisterCustomMetadata = (HRESULT(WINAPI *)( + PCWSTR))GetProcAddress(kernel32, "WerUnregisterCustomMetadata"); + } + if (!wer_data->WerRegisterCustomMetadata) { + SENTRY_DEBUG("WerRegisterCustomMetadata not available; " + "tag sync to WER will be skipped"); + } +} + +static void +sentry_integration_wer_free(void *data) +{ + sentry_free(data); +} + +static void +wer_set_tag(void *data, const char *key, size_t key_len, const char *value, + size_t value_len) +{ + sentry_integration_wer_data_t *wer_data + = (sentry_integration_wer_data_t *)data; + if (!wer_data->WerRegisterCustomMetadata) { + return; + } + if (!key || !value) { + return; + } + + char *key_n = sentry__string_clone_n(key, key_len); + char *value_n = sentry__string_clone_n(value, value_len); + wchar_t *key_w = sentry__string_to_wstr(key_n); + wchar_t *value_w = sentry__string_to_wstr(value_n); + sentry_free(key_n); + sentry_free(value_n); + + if (!key_w || !value_w) { + sentry_free(key_w); + sentry_free(value_w); + return; + } + + HRESULT hr = wer_data->WerRegisterCustomMetadata(key_w, value_w); + if (FAILED(hr)) { + SENTRY_WARNF( + "WerRegisterCustomMetadata failed: hr=0x%08lx", (unsigned long)hr); + } + + sentry_free(value_w); + sentry_free(key_w); +} + +static void +wer_remove_tag(void *data, const char *key, size_t key_len) +{ + sentry_integration_wer_data_t *wer_data + = (sentry_integration_wer_data_t *)data; + if (!wer_data->WerUnregisterCustomMetadata) { + return; + } + if (!key) { + return; + } + + char *key_n = sentry__string_clone_n(key, key_len); + wchar_t *key_w = sentry__string_to_wstr(key_n); + sentry_free(key_n); + + if (!key_w) { + return; + } + + HRESULT hr = wer_data->WerUnregisterCustomMetadata(key_w); + if (FAILED(hr)) { + SENTRY_WARNF("WerUnregisterCustomMetadata failed: hr=0x%08lx", + (unsigned long)hr); + } + + sentry_free(key_w); +} + +static void +wer_add_attachment(void *UNUSED(data), sentry_attachment_t *attachment) +{ + if (!attachment) { + return; + } + + if (attachment->path) { + const wchar_t *path_w = attachment->path->path_w; + if (!path_w) { + return; + } + HRESULT hr = WerRegisterFile( + path_w, WerRegFileTypeOther, WER_FILE_ANONYMOUS_DATA); + if (FAILED(hr)) { + SENTRY_WARNF( + "WerRegisterFile failed: hr=0x%08lx", (unsigned long)hr); + } + return; + } + + if (attachment->buf && attachment->buf_len > 0) { + if (attachment->buf_len > MAXDWORD) { + SENTRY_WARNF("WerRegisterMemoryBlock: buffer too large (%zu bytes)", + attachment->buf_len); + return; + } + HRESULT hr = WerRegisterMemoryBlock( + (PVOID)attachment->buf, (DWORD)attachment->buf_len); + if (FAILED(hr)) { + SENTRY_WARNF( + "WerRegisterMemoryBlock failed: hr=0x%08lx", (unsigned long)hr); + } + return; + } +} + +static void +wer_remove_attachment(void *UNUSED(data), sentry_attachment_t *attachment) +{ + if (!attachment) { + return; + } + + if (attachment->path) { + const wchar_t *path_w = attachment->path->path_w; + if (!path_w) { + return; + } + HRESULT hr = WerUnregisterFile(path_w); + if (FAILED(hr)) { + SENTRY_WARNF( + "WerUnregisterFile failed: hr=0x%08lx", (unsigned long)hr); + } + return; + } + + if (attachment->buf) { + HRESULT hr = WerUnregisterMemoryBlock((PVOID)attachment->buf); + if (FAILED(hr)) { + SENTRY_WARNF("WerUnregisterMemoryBlock failed: hr=0x%08lx", + (unsigned long)hr); + } + } +} + +static void +wer_cleanup_tag(const char *key, sentry_value_t UNUSED(value), void *data) +{ + wer_remove_tag(data, key, strlen(key)); +} + +static void +register_wer( + void *data, sentry_scope_t *scope, const sentry_options_t *UNUSED(options)) +{ + sentry_integration_wer_data_t *wer_data + = (sentry_integration_wer_data_t *)data; + wer_init(wer_data); + + sentry_scope_observer_t *observer = sentry__scope_observer_new(); + if (!observer) { + return; + } + observer->set_tag = wer_set_tag; + observer->remove_tag = wer_remove_tag; + observer->add_attachment = wer_add_attachment; + observer->remove_attachment = wer_remove_attachment; + observer->data = wer_data; + + if (sentry__scope_add_observer(scope, observer)) { + wer_data->observer = observer; + for (sentry_attachment_t *attachment = scope->attachments; attachment; + attachment = attachment->next) { + wer_add_attachment(wer_data, attachment); + } + } +} + +static void +unregister_wer( + void *data, sentry_scope_t *scope, const sentry_options_t *UNUSED(options)) +{ + sentry_integration_wer_data_t *wer_data + = (sentry_integration_wer_data_t *)data; + if (!wer_data->observer) { + return; + } + + sentry__value_foreach_key_value(scope->tags, wer_cleanup_tag, wer_data); + + for (sentry_attachment_t *attachment = scope->attachments; attachment; + attachment = attachment->next) { + wer_remove_attachment(wer_data, attachment); + } + + sentry__scope_remove_observer(scope, wer_data->observer); + wer_data->observer = NULL; +} + +sentry_integration_t * +sentry_integration_wer_new(void) +{ + sentry_integration_t *integration = SENTRY_MAKE(sentry_integration_t); + if (!integration) { + return NULL; + } + + integration->data = SENTRY_MAKE(sentry_integration_wer_data_t); + if (!integration->data) { + sentry_free(integration); + return NULL; + } + + integration->register_func = register_wer; + integration->unregister_func = unregister_wer; + integration->free_func = sentry_integration_wer_free; + + return integration; +} diff --git a/src/integrations/sentry_integration_wer.h b/src/integrations/sentry_integration_wer.h new file mode 100644 index 000000000..2fc00de08 --- /dev/null +++ b/src/integrations/sentry_integration_wer.h @@ -0,0 +1,11 @@ +#ifndef SENTRY_INTEGRATION_WER_H_INCLUDED +#define SENTRY_INTEGRATION_WER_H_INCLUDED + +#include "sentry_integration.h" + +/** + * Creates the WER integration. + */ +sentry_integration_t *sentry_integration_wer_new(void); + +#endif diff --git a/src/sentry_options.c b/src/sentry_options.c index f848a0487..7ea042851 100644 --- a/src/sentry_options.c +++ b/src/sentry_options.c @@ -17,6 +17,10 @@ # include "integrations/sentry_integration_qt.h" #endif +#ifdef SENTRY_INTEGRATION_WER +# include "integrations/sentry_integration_wer.h" +#endif + static double normalize_sample_rate(double sample_rate, double default_value) { @@ -126,6 +130,9 @@ sentry_options_new(void) #ifdef SENTRY_INTEGRATION_QT sentry__options_add_integration(opts, sentry_integration_qt_new()); #endif +#ifdef SENTRY_INTEGRATION_WER + sentry__options_add_integration(opts, sentry_integration_wer_new()); +#endif return opts; } diff --git a/tests/test_integration_wer.py b/tests/test_integration_wer.py new file mode 100644 index 000000000..3e5efc802 --- /dev/null +++ b/tests/test_integration_wer.py @@ -0,0 +1,187 @@ +import ctypes +from ctypes import wintypes +import os +import subprocess +import sys + +from pathlib import Path + +import pytest + +from . import run +from .assertions import wait_for +from .conditions import has_breakpad, has_crashpad, has_native, is_qemu + +pytestmark = [ + pytest.mark.skipif( + sys.platform != "win32" or bool(os.environ.get("TEST_MINGW")), + reason="WER integration tests are only available in MSVC Windows builds", + ), + pytest.mark.with_wer, +] + +S_OK = 0 + +E_STORE_USER_ARCHIVE = 0 +E_STORE_USER_QUEUE = 1 +E_STORE_MACHINE_ARCHIVE = 2 +E_STORE_MACHINE_QUEUE = 3 + + +class WerStore: + def __init__(self): + self._wer = ctypes.WinDLL("wer.dll") + self._wer.WerStoreOpen.argtypes = [ + ctypes.c_int, + ctypes.POINTER(wintypes.HANDLE), + ] + self._wer.WerStoreOpen.restype = ctypes.c_long + self._wer.WerStoreClose.argtypes = [wintypes.HANDLE] + self._wer.WerStoreClose.restype = None + self._wer.WerStoreGetFirstReportKey.argtypes = [ + wintypes.HANDLE, + ctypes.POINTER(ctypes.c_wchar_p), + ] + self._wer.WerStoreGetFirstReportKey.restype = ctypes.c_long + self._wer.WerStoreGetNextReportKey.argtypes = [ + wintypes.HANDLE, + ctypes.POINTER(ctypes.c_wchar_p), + ] + self._wer.WerStoreGetNextReportKey.restype = ctypes.c_long + self._wer.WerFreeString.argtypes = [ctypes.c_wchar_p] + self._wer.WerFreeString.restype = None + + def report_dirs(self): + for store_type in ( + E_STORE_USER_ARCHIVE, + E_STORE_USER_QUEUE, + E_STORE_MACHINE_ARCHIVE, + E_STORE_MACHINE_QUEUE, + ): + handle = wintypes.HANDLE() + hr = self._wer.WerStoreOpen(store_type, ctypes.byref(handle)) + if hr != S_OK: + continue + + try: + key = ctypes.c_wchar_p() + hr = self._wer.WerStoreGetFirstReportKey(handle, ctypes.byref(key)) + while hr == S_OK and key.value: + report_dir = key.value + self._wer.WerFreeString(key) + yield Path(report_dir) + + key = ctypes.c_wchar_p() + hr = self._wer.WerStoreGetNextReportKey(handle, ctypes.byref(key)) + finally: + self._wer.WerStoreClose(handle) + + +def wait_for_wer_report(store, test_id): + seen_report_dirs = 0 + readable_reports = 0 + matching_report = None + + def find_report(): + nonlocal matching_report + nonlocal seen_report_dirs + nonlocal readable_reports + + for report_dir in store.report_dirs(): + seen_report_dirs += 1 + report_path = report_dir / "Report.wer" + try: + report = report_path.read_text(encoding="utf-16-le") + except OSError: + continue + + readable_reports += 1 + report_lower = report.lower() + if "test.id" in report_lower and test_id in report: + matching_report = (report_path, report) + return True + + return False + + if wait_for(find_report): + return matching_report + + details = [ + f"searched {seen_report_dirs} WER report directories", + f"read {readable_reports} Report.wer files", + ] + pytest.fail( + f"WER report with test.id={test_id} was not found ({', '.join(details)})" + ) + + +@pytest.mark.parametrize( + "backend", + [ + "none", + "inproc", + pytest.param( + "breakpad", + marks=[ + pytest.mark.skipif( + not has_breakpad or is_qemu, reason="breakpad backend not available" + ), + pytest.mark.xfail( + reason="breakpad swallows the exception", + strict=True, + ), + ], + ), + pytest.param( + "crashpad", + marks=[ + pytest.mark.skipif( + not has_crashpad, reason="crashpad backend not available" + ), + pytest.mark.xfail( + reason="crashpad handler terminates the process", + strict=True, + ), + ], + ), + pytest.param( + "native", + marks=pytest.mark.skipif( + not has_native or is_qemu, reason="native backend not available" + ), + id="native", + ), + ], +) +def test_wer_custom_metadata(cmake, backend): + tmp_path = cmake( + ["sentry_example"], + { + "SENTRY_BACKEND": backend, + "SENTRY_TRANSPORT": "none", + "SENTRY_INTEGRATION_WER": "ON", + }, + ) + + completed = run( + tmp_path, + "sentry_example", + ["e2e-test", "crash"], + expect_failure=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + ) + + test_id = None + for line in completed.stdout.splitlines(): + if line.startswith("TEST_ID:"): + test_id = line.removeprefix("TEST_ID:") + break + assert test_id, completed.stdout + + report_path, report = wait_for_wer_report(WerStore(), test_id) + assert report_path.name == "Report.wer" + assert "expected-tag" in report + assert "some value" in report + assert "not-expected-tag" not in report From f5e185d605428ce79d4280af425cbaa61bef4978 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Tue, 21 Jul 2026 13:04:33 +0200 Subject: [PATCH 2/7] adapt to scope observer API changes --- src/integrations/sentry_integration_wer.c | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/src/integrations/sentry_integration_wer.c b/src/integrations/sentry_integration_wer.c index da968f0d2..1a347834e 100644 --- a/src/integrations/sentry_integration_wer.c +++ b/src/integrations/sentry_integration_wer.c @@ -47,8 +47,7 @@ sentry_integration_wer_free(void *data) } static void -wer_set_tag(void *data, const char *key, size_t key_len, const char *value, - size_t value_len) +wer_set_tag(void *data, const char *key, const char *value) { sentry_integration_wer_data_t *wer_data = (sentry_integration_wer_data_t *)data; @@ -59,13 +58,8 @@ wer_set_tag(void *data, const char *key, size_t key_len, const char *value, return; } - char *key_n = sentry__string_clone_n(key, key_len); - char *value_n = sentry__string_clone_n(value, value_len); - wchar_t *key_w = sentry__string_to_wstr(key_n); - wchar_t *value_w = sentry__string_to_wstr(value_n); - sentry_free(key_n); - sentry_free(value_n); - + wchar_t *key_w = sentry__string_to_wstr(key); + wchar_t *value_w = sentry__string_to_wstr(value); if (!key_w || !value_w) { sentry_free(key_w); sentry_free(value_w); @@ -83,7 +77,7 @@ wer_set_tag(void *data, const char *key, size_t key_len, const char *value, } static void -wer_remove_tag(void *data, const char *key, size_t key_len) +wer_remove_tag(void *data, const char *key) { sentry_integration_wer_data_t *wer_data = (sentry_integration_wer_data_t *)data; @@ -94,10 +88,7 @@ wer_remove_tag(void *data, const char *key, size_t key_len) return; } - char *key_n = sentry__string_clone_n(key, key_len); - wchar_t *key_w = sentry__string_to_wstr(key_n); - sentry_free(key_n); - + wchar_t *key_w = sentry__string_to_wstr(key); if (!key_w) { return; } @@ -180,7 +171,7 @@ wer_remove_attachment(void *UNUSED(data), sentry_attachment_t *attachment) static void wer_cleanup_tag(const char *key, sentry_value_t UNUSED(value), void *data) { - wer_remove_tag(data, key, strlen(key)); + wer_remove_tag(data, key); } static void From d2fefdf9abd7b6e64d224010e7e6a057c3ec2825 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Tue, 21 Jul 2026 13:20:21 +0200 Subject: [PATCH 3/7] clear --- src/integrations/sentry_integration_wer.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/integrations/sentry_integration_wer.c b/src/integrations/sentry_integration_wer.c index 1a347834e..6d9ced2f9 100644 --- a/src/integrations/sentry_integration_wer.c +++ b/src/integrations/sentry_integration_wer.c @@ -18,6 +18,7 @@ typedef struct sentry_integration_wer_data_s { HRESULT(WINAPI *WerRegisterCustomMetadata)(PCWSTR, PCWSTR); HRESULT(WINAPI *WerUnregisterCustomMetadata)(PCWSTR); + sentry_scope_t *scope; sentry_scope_observer_t *observer; } sentry_integration_wer_data_t; @@ -174,6 +175,24 @@ wer_cleanup_tag(const char *key, sentry_value_t UNUSED(value), void *data) wer_remove_tag(data, key); } +static void +wer_clear(void *data) +{ + sentry_integration_wer_data_t *wer_data + = (sentry_integration_wer_data_t *)data; + sentry_scope_t *scope = wer_data->scope; + if (!scope) { + return; + } + + sentry__value_foreach_key_value(scope->tags, wer_cleanup_tag, wer_data); + + for (sentry_attachment_t *attachment = scope->attachments; attachment; + attachment = attachment->next) { + wer_remove_attachment(wer_data, attachment); + } +} + static void register_wer( void *data, sentry_scope_t *scope, const sentry_options_t *UNUSED(options)) @@ -186,6 +205,7 @@ register_wer( if (!observer) { return; } + observer->clear = wer_clear; observer->set_tag = wer_set_tag; observer->remove_tag = wer_remove_tag; observer->add_attachment = wer_add_attachment; @@ -193,6 +213,7 @@ register_wer( observer->data = wer_data; if (sentry__scope_add_observer(scope, observer)) { + wer_data->scope = scope; wer_data->observer = observer; for (sentry_attachment_t *attachment = scope->attachments; attachment; attachment = attachment->next) { @@ -219,6 +240,7 @@ unregister_wer( } sentry__scope_remove_observer(scope, wer_data->observer); + wer_data->scope = NULL; wer_data->observer = NULL; } From cdf9193ed8c1c33f0f5dbfca1c642e7315074f95 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 22 Jul 2026 23:18:13 +0200 Subject: [PATCH 4/7] resolve absolute paths --- src/integrations/sentry_integration_wer.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/integrations/sentry_integration_wer.c b/src/integrations/sentry_integration_wer.c index 6d9ced2f9..e458e0bd0 100644 --- a/src/integrations/sentry_integration_wer.c +++ b/src/integrations/sentry_integration_wer.c @@ -4,6 +4,7 @@ #include "sentry_attachment.h" #include "sentry_core.h" #include "sentry_logger.h" +#include "sentry_path.h" #include "sentry_scope.h" #include "sentry_string.h" @@ -111,16 +112,17 @@ wer_add_attachment(void *UNUSED(data), sentry_attachment_t *attachment) } if (attachment->path) { - const wchar_t *path_w = attachment->path->path_w; - if (!path_w) { + sentry_path_t *path = sentry__path_absolute(attachment->path); + if (!path) { return; } HRESULT hr = WerRegisterFile( - path_w, WerRegFileTypeOther, WER_FILE_ANONYMOUS_DATA); + path->path_w, WerRegFileTypeOther, WER_FILE_ANONYMOUS_DATA); if (FAILED(hr)) { SENTRY_WARNF( "WerRegisterFile failed: hr=0x%08lx", (unsigned long)hr); } + sentry__path_free(path); return; } @@ -148,15 +150,16 @@ wer_remove_attachment(void *UNUSED(data), sentry_attachment_t *attachment) } if (attachment->path) { - const wchar_t *path_w = attachment->path->path_w; - if (!path_w) { + sentry_path_t *path = sentry__path_absolute(attachment->path); + if (!path) { return; } - HRESULT hr = WerUnregisterFile(path_w); + HRESULT hr = WerUnregisterFile(path->path_w); if (FAILED(hr)) { SENTRY_WARNF( "WerUnregisterFile failed: hr=0x%08lx", (unsigned long)hr); } + sentry__path_free(path); return; } From af741ab9a0214ef7ad014524149bcb9e40551d2d Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 22 Jul 2026 23:23:18 +0200 Subject: [PATCH 5/7] WER_MAX_MEM_BLOCK_SIZE --- src/integrations/sentry_integration_wer.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/integrations/sentry_integration_wer.c b/src/integrations/sentry_integration_wer.c index e458e0bd0..620d83e99 100644 --- a/src/integrations/sentry_integration_wer.c +++ b/src/integrations/sentry_integration_wer.c @@ -15,6 +15,9 @@ #ifndef WER_FILE_ANONYMOUS_DATA # define WER_FILE_ANONYMOUS_DATA 0x2 #endif +#ifndef WER_MAX_MEM_BLOCK_SIZE +# define WER_MAX_MEM_BLOCK_SIZE (64 * 1024) +#endif typedef struct sentry_integration_wer_data_s { HRESULT(WINAPI *WerRegisterCustomMetadata)(PCWSTR, PCWSTR); @@ -127,7 +130,7 @@ wer_add_attachment(void *UNUSED(data), sentry_attachment_t *attachment) } if (attachment->buf && attachment->buf_len > 0) { - if (attachment->buf_len > MAXDWORD) { + if (attachment->buf_len > WER_MAX_MEM_BLOCK_SIZE) { SENTRY_WARNF("WerRegisterMemoryBlock: buffer too large (%zu bytes)", attachment->buf_len); return; From 9d7d6ec6f42070fb8ea25495646d31a7156c9522 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Thu, 23 Jul 2026 10:28:22 +0200 Subject: [PATCH 6/7] sanity check max lengths and sizes --- src/integrations/sentry_integration_wer.c | 32 +++++++++++++++-------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/src/integrations/sentry_integration_wer.c b/src/integrations/sentry_integration_wer.c index 620d83e99..e3686851e 100644 --- a/src/integrations/sentry_integration_wer.c +++ b/src/integrations/sentry_integration_wer.c @@ -9,15 +9,25 @@ #include "sentry_string.h" #include +#include #include // Windows 8+ SDK #ifndef WER_FILE_ANONYMOUS_DATA # define WER_FILE_ANONYMOUS_DATA 0x2 #endif +#ifndef WER_MAX_PARAM_LENGTH +# define WER_MAX_PARAM_LENGTH (MAX_PATH) +#endif #ifndef WER_MAX_MEM_BLOCK_SIZE # define WER_MAX_MEM_BLOCK_SIZE (64 * 1024) #endif +#ifndef WER_METADATA_KEY_MAX_LENGTH +# define WER_METADATA_KEY_MAX_LENGTH 64 +#endif +#ifndef WER_METADATA_VALUE_MAX_LENGTH +# define WER_METADATA_VALUE_MAX_LENGTH 128 +#endif typedef struct sentry_integration_wer_data_s { HRESULT(WINAPI *WerRegisterCustomMetadata)(PCWSTR, PCWSTR); @@ -65,7 +75,8 @@ wer_set_tag(void *data, const char *key, const char *value) wchar_t *key_w = sentry__string_to_wstr(key); wchar_t *value_w = sentry__string_to_wstr(value); - if (!key_w || !value_w) { + if (!key_w || !value_w || wcslen(key_w) > WER_METADATA_KEY_MAX_LENGTH + || wcslen(value_w) > WER_METADATA_VALUE_MAX_LENGTH) { sentry_free(key_w); sentry_free(value_w); return; @@ -77,8 +88,8 @@ wer_set_tag(void *data, const char *key, const char *value) "WerRegisterCustomMetadata failed: hr=0x%08lx", (unsigned long)hr); } - sentry_free(value_w); sentry_free(key_w); + sentry_free(value_w); } static void @@ -94,7 +105,8 @@ wer_remove_tag(void *data, const char *key) } wchar_t *key_w = sentry__string_to_wstr(key); - if (!key_w) { + if (!key_w || wcslen(key_w) > WER_METADATA_KEY_MAX_LENGTH) { + sentry_free(key_w); return; } @@ -116,7 +128,8 @@ wer_add_attachment(void *UNUSED(data), sentry_attachment_t *attachment) if (attachment->path) { sentry_path_t *path = sentry__path_absolute(attachment->path); - if (!path) { + if (!path || wcslen(path->path_w) > WER_MAX_PARAM_LENGTH) { + sentry__path_free(path); return; } HRESULT hr = WerRegisterFile( @@ -129,12 +142,8 @@ wer_add_attachment(void *UNUSED(data), sentry_attachment_t *attachment) return; } - if (attachment->buf && attachment->buf_len > 0) { - if (attachment->buf_len > WER_MAX_MEM_BLOCK_SIZE) { - SENTRY_WARNF("WerRegisterMemoryBlock: buffer too large (%zu bytes)", - attachment->buf_len); - return; - } + if (attachment->buf && attachment->buf_len > 0 + && attachment->buf_len <= WER_MAX_MEM_BLOCK_SIZE) { HRESULT hr = WerRegisterMemoryBlock( (PVOID)attachment->buf, (DWORD)attachment->buf_len); if (FAILED(hr)) { @@ -154,7 +163,8 @@ wer_remove_attachment(void *UNUSED(data), sentry_attachment_t *attachment) if (attachment->path) { sentry_path_t *path = sentry__path_absolute(attachment->path); - if (!path) { + if (!path || wcslen(path->path_w) > WER_MAX_PARAM_LENGTH) { + sentry__path_free(path); return; } HRESULT hr = WerUnregisterFile(path->path_w); From 9a2b04221ecdb3c035060d803d9dcdab4303c3e3 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Thu, 23 Jul 2026 10:35:24 +0200 Subject: [PATCH 7/7] another WER_MAX_MEM_BLOCK_SIZE --- src/integrations/sentry_integration_wer.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/integrations/sentry_integration_wer.c b/src/integrations/sentry_integration_wer.c index e3686851e..c83a39579 100644 --- a/src/integrations/sentry_integration_wer.c +++ b/src/integrations/sentry_integration_wer.c @@ -176,7 +176,8 @@ wer_remove_attachment(void *UNUSED(data), sentry_attachment_t *attachment) return; } - if (attachment->buf) { + if (attachment->buf && attachment->buf_len > 0 + && attachment->buf_len <= WER_MAX_MEM_BLOCK_SIZE) { HRESULT hr = WerUnregisterMemoryBlock((PVOID)attachment->buf); if (FAILED(hr)) { SENTRY_WARNF("WerUnregisterMemoryBlock failed: hr=0x%08lx",