Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
**Features**

- Android: Expose app-hang detection through the NDK bindings via `NdkOptions.setEnableAppHangTracking()` and `NdkOptions.setAppHangTimeoutMillis()`. ([#1823](https://github.com/getsentry/sentry-native/pull/1823))
- Native/Windows: Allow configuring whether SEH and fast-fail crashes propagate to WER via `sentry_options_set_wer_mode`. ([#1833](https://github.com/getsentry/sentry-native/pull/1833))

## 0.15.2

Expand Down
45 changes: 45 additions & 0 deletions include/sentry.h
Original file line number Diff line number Diff line change
Expand Up @@ -1116,6 +1116,36 @@ typedef enum {
SENTRY_CACHE_KEEP_ALWAYS = 2,
} sentry_cache_keep_t;

/**
* Windows Error Reporting (WER) mode.
*/
typedef enum {
/**
* SEH crashes: the SDK's unhandled exception filter returns
* `EXCEPTION_EXECUTE_HANDLER`, the process is terminated, and the crash is
* not propagated to WER.
* Fast-fail crashes: no WER module is registered, so fast-fail crashes are
* not captured by Sentry, and are handled by WER instead.
*/
SENTRY_WER_MODE_NONE = 0,
/**
* SEH crashes: the SDK's unhandled exception filter returns
* `EXCEPTION_EXECUTE_HANDLER`, the process is terminated, and the crash is
* not propagated to WER.
* Fast-fail crashes: the WER module claims ownership and terminates the
* process, preventing WER to also process them.
*/
SENTRY_WER_MODE_EXCLUSIVE = 1,
/**
* SEH crashes: the SDK's unhandled exception filter returns
* `EXCEPTION_CONTINUE_SEARCH`, which proceeds with normal exception
* handling and lets WER also process the crash.
* Fast-fail crashes: the WER module does not claim ownership, allowing WER
* to also process them.
*/
SENTRY_WER_MODE_SHARED = 2,
Comment on lines +1140 to +1146

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The SEH crash handler crash_exception_filter unconditionally returns EXCEPTION_CONTINUE_SEARCH, ignoring the wer_mode for SENTRY_WER_MODE_EXCLUSIVE and SENTRY_WER_MODE_NONE.
Severity: HIGH

Suggested Fix

Add logic to the crash_exception_filter function to check ctx->wer_mode. It should return EXCEPTION_EXECUTE_HANDLER for SENTRY_WER_MODE_EXCLUSIVE and SENTRY_WER_MODE_NONE, and EXCEPTION_CONTINUE_SEARCH for SENTRY_WER_MODE_SHARED.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: include/sentry.h#L1140-L1146

Potential issue: The SEH crash handler in `src/backends/native/sentry_crash_handler.c`,
specifically the function `crash_exception_filter`, unconditionally returns
`EXCEPTION_CONTINUE_SEARCH`. This contradicts the documented behavior for
`SENTRY_WER_MODE_EXCLUSIVE` and `SENTRY_WER_MODE_NONE`, which require the function to
return `EXCEPTION_EXECUTE_HANDLER` to prevent Windows Error Reporting (WER) from
processing the crash. The implementation fails to check the `ctx->wer_mode`, thus
violating the API contract and causing incorrect WER interop behavior for SEH crashes on
Windows.

Did we get this right? 👍 / 👎 to inform future reviews.

} sentry_wer_mode_t;

/**
* Creates a new options struct.
* Can be freed with `sentry_options_free`.
Expand Down Expand Up @@ -1924,6 +1954,21 @@ SENTRY_API void sentry_options_set_crash_upload_mode(
SENTRY_API sentry_crash_upload_mode_t sentry_options_get_crash_upload_mode(
const sentry_options_t *opts);

/**
* Sets the WER (Windows Error Reporting) mode for the native backend.
*
* This setting only has an effect when using the `native` backend.
* Default is `SENTRY_WER_MODE_SHARED`.
*/
SENTRY_EXPERIMENTAL_API void sentry_options_set_wer_mode(
sentry_options_t *opts, sentry_wer_mode_t mode);

/**
* Gets the WER (Windows Error Reporting) mode.
*/
SENTRY_EXPERIMENTAL_API sentry_wer_mode_t sentry_options_get_wer_mode(
const sentry_options_t *opts);

/**
* Enables a wait for the crash report upload to be finished before shutting
* down. This is disabled by default.
Expand Down
1 change: 1 addition & 0 deletions src/backends/native/sentry_crash_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@ typedef struct {
uint64_t shutdown_timeout;
uint64_t transfer_timeout;
bool system_crash_reporter_enabled;
int wer_mode; // 0=none, 1=exclusive, 2=shared (see sentry_wer_mode_t)
uint32_t max_breadcrumbs;

// Atomic user consent (sentry_user_consent_t), updated whenever user
Expand Down
17 changes: 14 additions & 3 deletions src/backends/native/sentry_wer.c
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,16 @@ process_wer_exception(

InterlockedExchange(&ctx->state, SENTRY_CRASH_STATE_CRASHED);
if (SetEvent(event)) {
claimed = TRUE;
BOOL shared = ctx->wer_mode == SENTRY_WER_MODE_SHARED;
if (shared) {
// SHARED: signal the daemon asynchronously, but don't claim
// ownership so WER can also process the crash.
claimed = FALSE;
} else {
// EXCLUSIVE: wait for daemon and terminate process, claiming
// exclusive ownership of the crash.
claimed = TRUE;
}
uint64_t timeout_ms = ctx->shutdown_timeout
? ctx->shutdown_timeout
: SENTRY_CRASH_HANDLER_WAIT_TIMEOUT_MS;
Expand All @@ -161,8 +170,10 @@ process_wer_exception(
}
Sleep(SENTRY_CRASH_HANDLER_POLL_INTERVAL_MS);
}
TerminateProcess(exception_info->hProcess,
exception_info->exceptionRecord.ExceptionCode);
if (!shared) {
TerminateProcess(exception_info->hProcess,
exception_info->exceptionRecord.ExceptionCode);
}
}
}

Expand Down
5 changes: 4 additions & 1 deletion src/backends/sentry_backend_native.c
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,7 @@ native_backend_startup(
ctx->crash_reporting_mode = options->crash_reporting_mode;
ctx->system_crash_reporter_enabled = options->system_crash_reporter_enabled;
ctx->crash_upload_mode = options->crash_upload_mode;
ctx->wer_mode = options->wer_mode;

// Pass debug logging setting to daemon
ctx->debug_enabled = options->debug;
Expand Down Expand Up @@ -527,7 +528,9 @@ native_backend_startup(
}

# if defined(SENTRY_PLATFORM_WINDOWS) && !defined(SENTRY_PLATFORM_XBOX)
wer_register_module(tid);
if (options->wer_mode != SENTRY_WER_MODE_NONE) {
wer_register_module(tid);
}
# endif

if (sentry__crash_handler_init(state->ipc) < 0) {
Expand Down
19 changes: 19 additions & 0 deletions src/sentry_options.c
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ sentry_options_new(void)
= SENTRY_CRASH_REPORTING_MODE_NATIVE_WITH_MINIDUMP; // Default: best of
// both worlds
opts->crash_upload_mode = SENTRY_CRASH_UPLOAD_MODE_SYNC;
opts->wer_mode = SENTRY_WER_MODE_SHARED;
opts->enable_app_hang_tracking = false;
opts->app_hang_timeout = 5000;
opts->http_retry = false;
Expand Down Expand Up @@ -1038,6 +1039,24 @@ sentry_options_set_handler_strategy(

#endif // SENTRY_PLATFORM_LINUX

void
sentry_options_set_wer_mode(sentry_options_t *opts, sentry_wer_mode_t mode)
{
int imode = (int)mode;
if (imode < SENTRY_WER_MODE_NONE) {
imode = SENTRY_WER_MODE_NONE;
} else if (imode > SENTRY_WER_MODE_SHARED) {
imode = SENTRY_WER_MODE_SHARED;
}
opts->wer_mode = imode;
}

sentry_wer_mode_t
sentry_options_get_wer_mode(const sentry_options_t *opts)
{
return (sentry_wer_mode_t)opts->wer_mode;
}

void
sentry_options_set_http_retry(sentry_options_t *opts, int enabled)
{
Expand Down
1 change: 1 addition & 0 deletions src/sentry_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ struct sentry_options_s {
int crash_reporting_mode; // 0=minidump, 1=native, 2=native_with_minidump
// (see sentry_crash_reporting_mode_t)
int crash_upload_mode; // 0=sync, 1=async (see sentry_crash_upload_mode_t)
int wer_mode; // 0=none, 1=exclusive, 2=shared (see sentry_wer_mode_t)

#ifdef SENTRY_PLATFORM_NX
void (*network_connect_func)(void);
Expand Down
Loading