Skip to content

Commit b33ea57

Browse files
authored
fix: enforce span limit in curl_multi_exec and DDTrace\start_span code paths (#3691)
* fix: enforce span limit in curl_multi_exec and start_span code paths DD_TRACE_SPANS_LIMIT was correctly enforced in the hook system (uhook.c, uhook_legacy.c, uhook_attributes.c) but bypassed in three other code paths, causing unbounded span creation and OOM when curl_multi_exec is called repeatedly (e.g. AWS SDK SQS workloads). Missing checks: - dd_multi_inject_headers() (ext/handlers_curl.c): PHP 8 curl multi creates one INTERNAL_SPAN per handle without checking the limit. This is the primary path causing the reported customer OOM. - dd_inject_distributed_tracing_headers_multi() (ext/handlers_curl_php7.c): same issue on PHP 7. - dd_start_span() / DDTrace_start_trace_span() (ext/ddtrace.c): PHP-level DDTrace\start_span() and DDTrace\start_trace_span() APIs open real spans without checking the limit. Fix: add !ddtrace_tracer_is_limited() guards to each path. The curl multi paths fall through to header injection only (no span) when limited, preserving distributed tracing propagation. The PHP-level APIs return a dummy span (matching the disabled-tracing behaviour). Fixes: APMS-18744 * fix: revert span limit check from user-facing DDTrace\start_span APIs DDTrace\start_span() and DDTrace\start_trace_span() are user-facing APIs that intentionally bypass the span limit — the limit applies to auto-instrumentation (hooks) only. Guarding these APIs with ddtrace_tracer_is_limited() broke: - testTracerFlushedWhenSpanLimitExceeded: the test explicitly verifies that user-created spans work even when DD_TRACE_SPANS_LIMIT is hit. - Guzzle integration tests: isolateTracer() uses start_trace_span() internally to create isolated trace contexts; when closed_spans_count accumulated past the limit across PHPUnit tests, no new stack was created and all spans in the isolated test were lost. The curl multi code paths in handlers_curl.c and handlers_curl_php7.c remain fixed (the actual OOM culprit reported in APMS-18744).
1 parent 6bba842 commit b33ea57

2 files changed

Lines changed: 2 additions & 2 deletions

File tree

ext/handlers_curl.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ static void dd_multi_inject_headers(zend_object *mh) {
135135
if (handles && zend_hash_num_elements(handles) > 0) {
136136
zend_object *ch;
137137
ZEND_HASH_FOREACH_PTR(handles, ch) {
138-
if (DDTRACE_G(curl_multi_injecting_spans) && Z_TYPE(DDTRACE_G(curl_multi_injecting_spans)->val) == IS_ARRAY) {
138+
if (!ddtrace_tracer_is_limited() && DDTRACE_G(curl_multi_injecting_spans) && Z_TYPE(DDTRACE_G(curl_multi_injecting_spans)->val) == IS_ARRAY) {
139139
ddtrace_span_data *span = ddtrace_open_span(DDTRACE_INTERNAL_SPAN);
140140
dd_inject_distributed_tracing_headers(ch);
141141
ddtrace_observe_opened_span(span);

ext/handlers_curl_php7.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ static int dd_inject_distributed_tracing_headers(zval *ch) {
158158
}
159159

160160
static int dd_inject_distributed_tracing_headers_multi(zval *ch) {
161-
if (DDTRACE_G(curl_multi_injecting_spans) && Z_TYPE(DDTRACE_G(curl_multi_injecting_spans)->val) == IS_ARRAY) {
161+
if (!ddtrace_tracer_is_limited() && DDTRACE_G(curl_multi_injecting_spans) && Z_TYPE(DDTRACE_G(curl_multi_injecting_spans)->val) == IS_ARRAY) {
162162
ddtrace_span_data *span = ddtrace_open_span(DDTRACE_INTERNAL_SPAN);
163163
int ret = dd_inject_distributed_tracing_headers(ch);
164164
ddtrace_close_span(span);

0 commit comments

Comments
 (0)