Skip to content
Draft
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 @@ -10,6 +10,7 @@
- Add `on_crashed_last_run` callback for inspecting crash envelopes from previous runs. ([#1985](https://github.com/getsentry/sentry-native/pull/1985))
- Add `sentry_get_last_event_id` and `sentry_scope_get_last_event_id` for retrieving the last event ID captured with the global or given scope, respectively. ([#1992](https://github.com/getsentry/sentry-native/pull/1992))
- Native/Unix: The native crash daemon now loads `libcurl` dynamically at runtime by default when `SENTRY_LINK_CURL=AUTO`, avoiding `libcurl` linker work during process startup and significantly speeding up startup time. Explicitly set `SENTRY_LINK_CURL=ON` to link it directly. ([#1955](https://github.com/getsentry/sentry-native/pull/1955))
- Add `sentry_crash` for deliberately crashing the current process to test its crash reporting configuration. ([#2013](https://github.com/getsentry/sentry-native/pull/2013))

**Deprecations**:

Expand Down
34 changes: 2 additions & 32 deletions examples/example.c
Original file line number Diff line number Diff line change
Expand Up @@ -504,36 +504,6 @@ trigger_fastfail_crash()

#endif

#ifdef SENTRY_PLATFORM_AIX
// AIX has a null page mapped to the bottom of memory, which means null derefs
// don't segfault. try dereferencing the top of memory instead; the top nibble
// seems to be unusable.
static void *invalid_mem = (void *)0xFFFFFFFFFFFFFF9B; // -100 for memset
#else
static void *invalid_mem = (void *)1;
#endif

// Detect Address Sanitizer (works for both GCC and Clang)
#if defined(__SANITIZE_ADDRESS__)
# define SENTRY_ASAN_ACTIVE 1
#elif defined(__has_feature)
# if __has_feature(address_sanitizer)
# define SENTRY_ASAN_ACTIVE 1
# endif
#endif

static void
trigger_crash()
{
#ifdef SENTRY_ASAN_ACTIVE
// Under ASAN, raise signal directly to bypass ASAN's memory interception.
// ASAN intercepts memset and would abort before our signal handler runs.
raise(SIGSEGV);
#else
memset((char *)invalid_mem, 1, 100);
#endif
}

static void
trigger_stack_overflow()
{
Expand Down Expand Up @@ -1313,7 +1283,7 @@ main(int argc, char **argv)
}

if (has_arg(argc, argv, "crash")) {
trigger_crash();
sentry_crash();
}
if (has_arg(argc, argv, "stack-overflow")) {
trigger_stack_overflow();
Expand Down Expand Up @@ -1522,7 +1492,7 @@ main(int argc, char **argv)
}

if (has_arg(argc, argv, "crash-after-shutdown")) {
trigger_crash();
sentry_crash();
}

return EXIT_SUCCESS;
Expand Down
14 changes: 14 additions & 0 deletions include/sentry.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,16 +144,19 @@ extern "C" {
#endif

#if defined(__GNUC__) || defined(__clang__)
# define SENTRY_NORETURN __attribute__((noreturn))
# define SENTRY_SUPPRESS_DEPRECATED \
_Pragma("GCC diagnostic push"); \
_Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
# define SENTRY_RESTORE_DEPRECATED _Pragma("GCC diagnostic pop")
#elif defined(_MSC_VER)
# define SENTRY_NORETURN __declspec(noreturn)
# define SENTRY_SUPPRESS_DEPRECATED \
__pragma(warning(push)); \
__pragma(warning(disable : 4996))
# define SENTRY_RESTORE_DEPRECATED __pragma(warning(pop))
#else
# define SENTRY_NORETURN
# define SENTRY_SUPPRESS_DEPRECATED
# define SENTRY_RESTORE_DEPRECATED
#endif
Expand Down Expand Up @@ -2520,6 +2523,17 @@ SENTRY_API sentry_uuid_t sentry_capture_minidumpw_n(
SENTRY_EXPERIMENTAL_API void sentry_handle_exception(
const sentry_ucontext_t *uctx);

/**
* Deliberately crashes the current process.
*
* This is intended for testing that crash reporting is correctly configured.
* To capture the crash, call this only after `sentry_init` returns
* successfully.
*
* This function does not return and must not be used in production.
*/
SENTRY_EXPERIMENTAL_API SENTRY_NORETURN void sentry_crash(void);

/**
* Type of the `before_breadcrumb` callback.
*
Expand Down
33 changes: 33 additions & 0 deletions src/sentry_core.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#include "sentry_boot.h"

#include <signal.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>

#include "sentry_app_hang_latch.h"
Expand Down Expand Up @@ -929,6 +931,37 @@ sentry_handle_exception(const sentry_ucontext_t *uctx)
}
}

// Detect Address Sanitizer (works for both GCC and Clang).
#if defined(__SANITIZE_ADDRESS__)
# define SENTRY_ASAN_ACTIVE 1
#elif defined(__has_feature)
# if __has_feature(address_sanitizer)
# define SENTRY_ASAN_ACTIVE 1
# endif
#endif

void
sentry_crash(void)
{
#ifdef SENTRY_ASAN_ACTIVE
// ASAN intercepts memory writes and would abort before the crash handler
// runs, so bypass its memory instrumentation.
raise(SIGSEGV);
#else
# ifdef SENTRY_PLATFORM_AIX
// AIX maps its null page, so use an address near the top of memory.
void *volatile invalid_mem = (void *)0xFFFFFFFFFFFFFF9B;
# else
void *volatile invalid_mem = (void *)1;
# endif
memset((char *)invalid_mem, 1, 100);
#endif

// A user-installed signal or exception handler could allow execution to
// continue. Ensure this API never returns in that case.
abort();
}

sentry_uuid_t
sentry__new_event_id(void)
{
Expand Down
6 changes: 3 additions & 3 deletions tests/fixtures/dotnet_signal/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ namespace dotnet_signal;

class Program
{
[DllImport("crash", EntryPoint = "native_crash")]
static extern void native_crash();
[DllImport("sentry", EntryPoint = "sentry_crash")]
static extern void sentry_crash();

[DllImport("crash", EntryPoint = "enable_sigaltstack")]
static extern void enable_sigaltstack();
Expand Down Expand Up @@ -72,7 +72,7 @@ public static void RunTest(string[] args, string? databasePath = null)

if (args.Contains("native-crash"))
{
native_crash();
sentry_crash();
}
else if (args.Contains("managed-exception"))
{
Expand Down
4 changes: 0 additions & 4 deletions tests/fixtures/dotnet_signal/crash.c
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
#include <signal.h>
#include <stdlib.h>
void native_crash(void)
{
*(int *)10 = 100;
}

void enable_sigaltstack(void)
{
Expand Down
7 changes: 3 additions & 4 deletions tests/fixtures/inproc_stress/concurrent_crash.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
* race conditions in the signal handler / handler thread synchronization.
*/

#include <sentry.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifndef _WIN32
# include <pthread.h>
Expand All @@ -21,8 +22,6 @@

#define CRASH_THREADS 20

static void *invalid_mem = (void *)1;

// Barrier for synchronizing threads
#ifndef _WIN32
static volatile int g_barrier = 0;
Expand All @@ -40,7 +39,7 @@ __declspec(noinline)
void
do_crash(void)
{
memset((char *)invalid_mem, 1, 100);
sentry_crash();
}

static void
Expand Down
12 changes: 3 additions & 9 deletions tests/fixtures/inproc_stress/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -270,12 +270,6 @@ test_concurrent_crash(PATH_TYPE database_path)
return 1;
}

static void
trigger_crash(void)
{
memset((char *)invalid_mem, 1, 100);
}

static int
setup_sentry_with_crashing_on_crash(PATH_TYPE database_path)
{
Expand Down Expand Up @@ -330,7 +324,7 @@ test_handler_thread_crash(PATH_TYPE database_path)
// This will crash, trigger the handler thread, which will call
// on_crash callback, which will crash the handler thread.
// The fallback should then process in the signal handler.
trigger_crash();
sentry_crash();

fprintf(stderr, "ERROR: Should have crashed\n");
sentry_close();
Expand All @@ -352,7 +346,7 @@ test_handler_abort_crash(PATH_TYPE database_path)
// This will crash, trigger the handler thread, which will call
// on_crash callback, which will call abort(). abort() resets the
// signal mask, so this tests a different code path.
trigger_crash();
sentry_crash();

fprintf(stderr, "ERROR: Should have crashed\n");
sentry_close();
Expand Down Expand Up @@ -418,7 +412,7 @@ test_simple_crash(PATH_TYPE database_path)
fprintf(stderr, "Starting simple crash test\n");
fflush(stderr);

trigger_crash();
sentry_crash();

fprintf(stderr, "ERROR: Should have crashed\n");
sentry_close();
Expand Down
11 changes: 1 addition & 10 deletions tests/fixtures/screenshot/screenshot_win32.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

enum {
IDT_TIMER_CRASH = 1,
Expand All @@ -23,14 +22,6 @@ has_arg(int argc, LPWSTR *argv, LPCWSTR arg)
return false;
}

static void *invalid_mem = (void *)1;

static void
trigger_crash()
{
memset((char *)invalid_mem, 1, 100);
}

static void
trigger_stack_overflow()
{
Expand Down Expand Up @@ -61,7 +52,7 @@ wnd_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
case WM_TIMER:
switch (wParam) {
case IDT_TIMER_CRASH:
trigger_crash();
sentry_crash();
break;
case IDT_TIMER_STACK_OVERFLOW:
trigger_stack_overflow();
Expand Down
Loading