Skip to content
Draft
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
20 changes: 20 additions & 0 deletions ext/span.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "telemetry.h"
#include "ext/standard/php_string.h"
#include <hook/hook.h>
#include <signal.h>
#include "user_request.h"
#include "zend_types.h"
#include "sidecar.h"
Expand All @@ -37,6 +38,14 @@ static void dd_reset_span_counters(void) {
DDTRACE_G(closed_spans_count) = 0;
}

static zend_never_inline ZEND_COLD void ddtrace_crash_serialization_reentrancy(const char *where) {
LOG(ERROR, "Detected ddtrace serialization re-entrancy at %s; crashing intentionally for diagnosis", where);
/* Crash in a way that triggers crashtracking (SIGSEGV handler), not SIGABRT. */
raise(SIGSEGV);
/* If SIGSEGV is blocked or no handler is installed for some reason, hard-exit. */
_Exit(128 + SIGSEGV);
}

void ddtrace_init_span_stacks(void) {
DDTRACE_G(top_closed_stack) = NULL;
dd_reset_span_counters();
Expand Down Expand Up @@ -1091,6 +1100,15 @@ void ddtrace_drop_span(ddtrace_span_data *span) {
}

void ddtrace_serialize_closed_spans(ddog_TracesBytes *traces, bool fast_shutdown) {
// Prevent re-entrancy: serialization can be triggered from userland (explicit flush),
// from close paths (auto-flush), and indirectly via destructors/GC.
// Nested serialization risks use-after-free via iterator invalidation across many call sites.
static ZEND_TLS bool ddtrace_in_serialization = false;
if (ddtrace_in_serialization) {
ddtrace_crash_serialization_reentrancy("ddtrace_serialize_closed_spans");
}
ddtrace_in_serialization = true;

if (DDTRACE_G(top_closed_stack)) {
ddtrace_span_stack *rootstack = DDTRACE_G(top_closed_stack);
DDTRACE_G(top_closed_stack) = NULL;
Expand Down Expand Up @@ -1138,6 +1156,8 @@ void ddtrace_serialize_closed_spans(ddog_TracesBytes *traces, bool fast_shutdown
// Reset closed span counter for limit-refresh, don't touch open spans
DDTRACE_G(closed_spans_count) = 0;
DDTRACE_G(dropped_spans_count) = 0;

ddtrace_in_serialization = false;
}

void ddtrace_serialize_closed_spans_with_cycle(ddog_TracesBytes *traces, bool fast_shutdown) {
Expand Down
Loading