diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c05a177d..d5cb94a20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ - Native/macOS: fix thread stack descriptor. ([#1726](https://github.com/getsentry/sentry-native/pull/1726)) - Native/macOS: honor the `system_crash_reporter_enabled` option. ([#1743](https://github.com/getsentry/sentry-native/pull/1743)) - Cap rate-limit retry-after values at 24 hours to prevent a MITM-provided response from disabling event delivery for the process lifetime. ([#1744](https://github.com/getsentry/sentry-native/pull/1744)) +- Fix a shutdown-time use-after-free window in `sentry_close()`. ([#1750](https://github.com/getsentry/sentry-native/pull/1750)) - Native: validate ELF header entry sizes. ([#1746](https://github.com/getsentry/sentry-native/pull/1746)) - Structured logs: respect printf argument widths when extracting log parameters to avoid stack-data disclosure and corrupted attributes on 32-bit platforms. ([#1752](https://github.com/getsentry/sentry-native/pull/1752)) diff --git a/src/sentry_core.c b/src/sentry_core.c index 791020260..68afa4129 100644 --- a/src/sentry_core.c +++ b/src/sentry_core.c @@ -341,12 +341,12 @@ sentry_close(void) || !options->backend->can_capture_after_shutdown)) { sentry__run_clean(options->run, false); } + g_options = NULL; sentry_options_free(options); } else { SENTRY_WARN("sentry_close() called, but options was empty"); } - g_options = NULL; sentry__mutex_unlock(&g_options_lock); sentry__scope_cleanup(); diff --git a/tests/unit/test_basic.c b/tests/unit/test_basic.c index b8915d1af..9fa06b747 100644 --- a/tests/unit/test_basic.c +++ b/tests/unit/test_basic.c @@ -1,3 +1,5 @@ +#include "sentry_alloc.h" +#include "sentry_backend.h" #include "sentry_core.h" #include "sentry_database.h" #include "sentry_options.h" @@ -425,3 +427,30 @@ SENTRY_TEST(installation_id) sentry_free(id_a); } + +static void +backend_free_options_ref(sentry_backend_t *backend) +{ + const sentry_options_t **options_ref = backend->data; + *options_ref = sentry__options_getref(); +} + +SENTRY_TEST(clear_options) +{ + const sentry_options_t *options_ref = NULL; + + SENTRY_TEST_OPTIONS_NEW(options); + + sentry_backend_t *backend = SENTRY_MAKE(sentry_backend_t); + TEST_ASSERT(!!backend); + backend->free_func = backend_free_options_ref; + backend->data = &options_ref; + sentry_options_set_backend(options, backend); + + sentry_init(options); + sentry_close(); + + // The backend free hook runs from sentry_options_free(). At that point, + // sentry__options_getref() must no longer expose the options. + TEST_CHECK(options_ref == NULL); +} diff --git a/tests/unit/tests.inc b/tests/unit/tests.inc index 3529c6845..65de1eb09 100644 --- a/tests/unit/tests.inc +++ b/tests/unit/tests.inc @@ -76,6 +76,7 @@ XX(capture_minidump_without_sentry_init) XX(check_version) XX(child_spans) XX(child_spans_ts) +XX(clear_options) XX(client_report_concurrent) XX(client_report_discard) XX(client_report_discard_envelope)