diff --git a/ext/serializer.c b/ext/serializer.c index b9ec42a951..e2b2714e42 100644 --- a/ext/serializer.c +++ b/ext/serializer.c @@ -723,6 +723,14 @@ static void dd_set_entrypoint_root_span_props(struct superglob_equiv *data, ddtr } } +// Detects a zval that is flagged as refcounted but carries a NULL counted +// pointer. Copying such a zval with ZVAL_COPY_DEREF dereferences NULL inside +// GC_ADDREF and crashes the process. +static zend_always_inline bool dd_zval_is_broken_refcounted(zval *zv) { + ZVAL_DEREF(zv); + return Z_REFCOUNTED_P(zv) && Z_COUNTED_P(zv) == NULL; +} + void ddtrace_inherit_span_properties(ddtrace_span_data *span, ddtrace_span_data *parent) { zval *prop_service = &span->property_service; zval_ptr_dtor(prop_service); @@ -733,7 +741,13 @@ void ddtrace_inherit_span_properties(ddtrace_span_data *span, ddtrace_span_data zval *prop_baggage = &span->property_baggage, *prop_parent_baggage = &parent->property_baggage; zval_ptr_dtor(prop_baggage); - ZVAL_COPY_DEREF(prop_baggage, prop_parent_baggage); + if (UNEXPECTED(dd_zval_is_broken_refcounted(prop_parent_baggage))) { + // Fall back to the property's declared default instead of dereferencing + // a NULL refcounted pointer; baggage stays a valid array downstream. + ZVAL_EMPTY_ARRAY(prop_baggage); + } else { + ZVAL_COPY_DEREF(prop_baggage, prop_parent_baggage); + } zend_array *parent_meta = ddtrace_property_array(&parent->property_meta);