From b6f3be45f31586845707a31a85bbf26ea09ceea3 Mon Sep 17 00:00:00 2001 From: Ivan Tustanivskyi Date: Wed, 29 Apr 2026 14:58:16 +0300 Subject: [PATCH 1/4] fix(native): preserve attachments added during crash handling --- src/backends/sentry_backend_native.c | 101 +++++++++++++++------------ 1 file changed, 58 insertions(+), 43 deletions(-) diff --git a/src/backends/sentry_backend_native.c b/src/backends/sentry_backend_native.c index 9d43d6c3a..02135a91b 100644 --- a/src/backends/sentry_backend_native.c +++ b/src/backends/sentry_backend_native.c @@ -584,6 +584,57 @@ native_backend_free(sentry_backend_t *backend) sentry_free(state); } +// Writes the scope's attachment list to /__sentry-attachments so the +// crash daemon can locate and append them to the crash envelope. +static void +native_backend_write_attachments(const sentry_path_t *event_path) +{ + if (!event_path) { + return; + } + SENTRY_WITH_SCOPE (scope) { + if (!scope->attachments) { + continue; + } + sentry_path_t *run_path = sentry__path_dir(event_path); + if (!run_path) { + continue; + } + sentry_path_t *attach_list_path + = sentry__path_join_str(run_path, "__sentry-attachments"); + if (attach_list_path) { + sentry_value_t attach_list = sentry_value_new_list(); + for (sentry_attachment_t *it = scope->attachments; it; + it = it->next) { + if (!it->path) { + continue; + } + sentry_value_t attach_info = sentry_value_new_object(); + sentry_value_set_by_key(attach_info, "path", + sentry_value_new_string(it->path->path)); + const char *filename = sentry__path_filename( + it->filename ? it->filename : it->path); + sentry_value_set_by_key(attach_info, "filename", + sentry_value_new_string(filename)); + if (it->content_type) { + sentry_value_set_by_key(attach_info, "content_type", + sentry_value_new_string(it->content_type)); + } + sentry_value_append(attach_list, attach_info); + } + char *attach_json = sentry_value_to_json(attach_list); + sentry_value_decref(attach_list); + if (attach_json) { + sentry__path_write_buffer( + attach_list_path, attach_json, strlen(attach_json)); + sentry_free(attach_json); + } + sentry__path_free(attach_list_path); + } + sentry__path_free(run_path); + } +} + static void native_backend_flush_scope( sentry_backend_t *backend, const sentry_options_t *UNUSED(options)) @@ -660,49 +711,7 @@ native_backend_flush_scope( sentry_free(json_str); } - // Write attachment metadata (paths and filenames) so crash daemon can find - // them - SENTRY_WITH_SCOPE (scope) { - if (scope->attachments) { - sentry_path_t *run_path = sentry__path_dir(state->event_path); - if (run_path) { - sentry_path_t *attach_list_path - = sentry__path_join_str(run_path, "__sentry-attachments"); - if (attach_list_path) { - // Write attachment list as JSON array - sentry_value_t attach_list = sentry_value_new_list(); - for (sentry_attachment_t *it = scope->attachments; it; - it = it->next) { - if (it->path) { - sentry_value_t attach_info - = sentry_value_new_object(); - sentry_value_set_by_key(attach_info, "path", - sentry_value_new_string(it->path->path)); - const char *filename = sentry__path_filename( - it->filename ? it->filename : it->path); - sentry_value_set_by_key(attach_info, "filename", - sentry_value_new_string(filename)); - if (it->content_type) { - sentry_value_set_by_key(attach_info, - "content_type", - sentry_value_new_string(it->content_type)); - } - sentry_value_append(attach_list, attach_info); - } - } - char *attach_json = sentry_value_to_json(attach_list); - sentry_value_decref(attach_list); - if (attach_json) { - sentry__path_write_buffer( - attach_list_path, attach_json, strlen(attach_json)); - sentry_free(attach_json); - } - sentry__path_free(attach_list_path); - } - sentry__path_free(run_path); - } - } - } + native_backend_write_attachments(state->event_path); } static void @@ -923,6 +932,12 @@ native_backend_except(sentry_backend_t *backend, const sentry_ucontext_t *uctx) } #endif + // Re-emit the attachment manifest so any attachment + // registered by on_crash/before_send reaches the daemon. + if (state) { + native_backend_write_attachments(state->event_path); + } + // Write event as JSON file // Daemon will read this and create envelope with minidump if (state && state->event_path) { From 21c2631d7854758a2c5c416bee986c479e5e63f1 Mon Sep 17 00:00:00 2001 From: Ivan Tustanivskyi Date: Wed, 29 Apr 2026 15:22:48 +0300 Subject: [PATCH 2/4] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index de36b19f2..546ad1136 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - Fix event ownership (potential double-decref) in sentry_capture_minidump. ([#1669](https://github.com/getsentry/sentry-native/pull/1669)) - Guard against internal stringbuilder append and reserve size overflows. ([#1672](https://github.com/getsentry/sentry-native/pull/1672)) +- Preserve attachments added during crash handling ([#1687](https://github.com/getsentry/sentry-native/pull/1687)) ## 0.13.8 From b80c2e440d4e30f20950ae73538b784e347376cc Mon Sep 17 00:00:00 2001 From: Ivan Tustanivskyi Date: Wed, 29 Apr 2026 15:25:21 +0300 Subject: [PATCH 3/4] Fix lint errors --- src/backends/sentry_backend_native.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/backends/sentry_backend_native.c b/src/backends/sentry_backend_native.c index 02135a91b..5885ad656 100644 --- a/src/backends/sentry_backend_native.c +++ b/src/backends/sentry_backend_native.c @@ -614,8 +614,8 @@ native_backend_write_attachments(const sentry_path_t *event_path) sentry_value_new_string(it->path->path)); const char *filename = sentry__path_filename( it->filename ? it->filename : it->path); - sentry_value_set_by_key(attach_info, "filename", - sentry_value_new_string(filename)); + sentry_value_set_by_key( + attach_info, "filename", sentry_value_new_string(filename)); if (it->content_type) { sentry_value_set_by_key(attach_info, "content_type", sentry_value_new_string(it->content_type)); From 5d0f3c8718c60cf3d82cceea86d5b46e9a0de69b Mon Sep 17 00:00:00 2001 From: Ivan Tustanivskyi Date: Wed, 29 Apr 2026 18:14:17 +0300 Subject: [PATCH 4/4] Keep attachment writes in flush_scope --- src/backends/sentry_backend_native.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/backends/sentry_backend_native.c b/src/backends/sentry_backend_native.c index 5885ad656..78d436784 100644 --- a/src/backends/sentry_backend_native.c +++ b/src/backends/sentry_backend_native.c @@ -640,7 +640,15 @@ native_backend_flush_scope( sentry_backend_t *backend, const sentry_options_t *UNUSED(options)) { native_backend_state_t *state = (native_backend_state_t *)backend->data; - if (!state || !state->event_path || sentry__atomic_fetch(&state->crashed)) { + if (!state || !state->event_path) { + return; + } + + // Manifest writes must continue post-crash so attachments registered + // from on_crash/before_send reach the daemon + native_backend_write_attachments(state->event_path); + + if (sentry__atomic_fetch(&state->crashed)) { return; } @@ -710,8 +718,6 @@ native_backend_flush_scope( sentry__path_write_buffer(state->event_path, json_str, json_len); sentry_free(json_str); } - - native_backend_write_attachments(state->event_path); } static void @@ -932,12 +938,6 @@ native_backend_except(sentry_backend_t *backend, const sentry_ucontext_t *uctx) } #endif - // Re-emit the attachment manifest so any attachment - // registered by on_crash/before_send reaches the daemon. - if (state) { - native_backend_write_attachments(state->event_path); - } - // Write event as JSON file // Daemon will read this and create envelope with minidump if (state && state->event_path) {