Skip to content

fix: Guard span baggage inheritance against malformed parent zval#3943

Open
jdani-airalo wants to merge 1 commit into
DataDog:masterfrom
jdani-airalo:harden-baggage-span-inheritance
Open

fix: Guard span baggage inheritance against malformed parent zval#3943
jdani-airalo wants to merge 1 commit into
DataDog:masterfrom
jdani-airalo:harden-baggage-span-inheritance

Conversation

@jdani-airalo
Copy link
Copy Markdown

@jdani-airalo jdani-airalo commented Jun 2, 2026

Generated with AI assistance and reviewed before submission. (Adding the "AI Generated" label here would require triage permission on this repo, which external contributors do not have — flagging it in the description instead.)

Description

ddtrace_inherit_span_properties (ext/serializer.c) copies several parent span
properties into a new span with ZVAL_COPY_DEREF. That macro runs GC_ADDREF on
the source whenever its type carries the refcounted flag, without checking that the
counted pointer is valid.

When the parent's baggage property is a zval whose type is flagged refcounted but
whose Z_COUNTED pointer is NULL, GC_ADDREF dereferences NULL and the process
crashes with a SIGSEGV during span creation.

Crash signature observed in production (PHP 8.3, NTS, fpm-fcgi, tracer 1.20.0):

  • SIGSEGV at the GC_ADDREF increment (addl $0x1,(%rdx) with %rdx = 0).
  • addr2line on the faulting offset points into ddtrace_inherit_span_properties.
    The function issues five back-to-back zval_ptr_dtor(); ZVAL_COPY_DEREF();
    sequences; at -O2 these identical tails fold together, so the reported line
    lands on the first copy regardless of which property actually faults.
  • The crashes stop when baggage extraction is disabled
    (DD_TRACE_PROPAGATION_STYLE=datadog,tracecontext). The other inherited
    properties (service, type, env, version) are long-stable strings; baggage
    is the only inherited field populated from inbound distributed-tracing headers and
    managed as an array. That points at the baggage copy as the faulting one.

Change

Guard the baggage copy. If the parent baggage zval is flagged refcounted but holds a
NULL counted pointer, skip the copy and initialize the new span's baggage to its
declared empty-array default instead of dereferencing NULL. Valid baggage is copied
exactly as before, so propagation behavior is unchanged for well-formed input.

The guard is scoped to the baggage property only, where the evidence points, and adds
no PHP-version or ZTS-specific branches.

Notes

This is a defensive fix at the crash site. The producer path that leaves the baggage
zval in the refcounted-with-NULL state is not pinned down here: the visible baggage
write paths (header merge, apply_baggage_span_tags, the serializer copy via
zend_hash_copy(..., zval_add_ref)) look refcount-balanced. The crash is reproducible
in production under load but not yet as a deterministic unit test, so no regression
test is included rather than adding a flaky one. Happy to add a harness test if you can
point at a reliable trigger, or to move the guard to the producer if you can identify
the exact path.

Reviewer checklist

  • Test coverage seems ok.
  • Appropriate labels assigned.

ddtrace_inherit_span_properties copies the parent span's baggage property
with ZVAL_COPY_DEREF, which runs GC_ADDREF unconditionally when the source
is flagged refcounted. When the parent baggage zval carries the refcounted
flag but a NULL counted pointer, GC_ADDREF dereferences NULL and the process
takes a SIGSEGV during span creation.

Skip the copy and fall back to the property's declared empty-array default
when the source zval is in that broken state. Valid baggage is unaffected.

Signed-off-by: Dani Jimenez <daniel.jimenez@airalo.com>
@jdani-airalo jdani-airalo requested a review from a team as a code owner June 2, 2026 11:52
@jdani-airalo jdani-airalo changed the title Guard span baggage inheritance against malformed parent zval fix: Guard span baggage inheritance against malformed parent zval Jun 2, 2026
@bwoebi
Copy link
Copy Markdown
Collaborator

bwoebi commented Jun 3, 2026

Hey @jdani-airalo.

the big question is - how does that become NULL. This is an invalid state and probably direct access to \DDTrace\active_span()->baggage in very normal user code would also crash then.

I was unable to pin down a possible path to get this to crash. Do have any reproducer? Any information your AI could extract from the core dump / source code to reconstruct a path to getting this crashing?

This PR is a band-aid, not a proper fix :-(

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants