Skip to content
Merged
Show file tree
Hide file tree
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
2 changes: 2 additions & 0 deletions ext/compatibility.h
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,8 @@ static inline HashTable *zend_new_array(uint32_t nSize) {
((zend_object*)((char*)(op_array) - sizeof(zend_object)))

#define zend_std_read_dimension std_object_handlers.read_dimension
#define zend_std_get_property_ptr_ptr std_object_handlers.get_property_ptr_ptr
#define zend_std_read_property std_object_handlers.read_property

// make ZEND_STRL work
#undef zend_hash_str_update
Expand Down
33 changes: 33 additions & 0 deletions ext/ddtrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -1224,6 +1224,37 @@ static zval *ddtrace_root_span_data_write(zend_object *object, zend_string *memb
#endif
}

#if PHP_VERSION_ID < 80000
static zval *ddtrace_span_stack_read_property(zval *object, zval *member, int type, void **cache_slot, zval *rv) {
zend_string *prop_name = Z_TYPE_P(member) == IS_STRING ? Z_STR_P(member) : ZSTR_EMPTY_ALLOC();
ddtrace_span_stack *stack = (ddtrace_span_stack *)Z_OBJ_P(object);
#else
static zval *ddtrace_span_stack_read_property(zend_object *object, zend_string *member, int type, void **cache_slot, zval *rv) {
zend_string *prop_name = member;
ddtrace_span_stack *stack = (ddtrace_span_stack *)object;
#endif
if ((type == BP_VAR_W || type == BP_VAR_RW || type == BP_VAR_UNSET)
&& zend_string_equals_literal(prop_name, "active")) {
ZVAL_COPY(rv, &stack->property_active);
return rv;
}
return zend_std_read_property(object, member, type, cache_slot, rv);
}

#if PHP_VERSION_ID < 80000
static zval *ddtrace_span_stack_get_property_ptr_ptr(zval *object, zval *member, int type, void **cache_slot) {
zend_string *prop_name = Z_TYPE_P(member) == IS_STRING ? Z_STR_P(member) : ZSTR_EMPTY_ALLOC();
#else
static zval *ddtrace_span_stack_get_property_ptr_ptr(zend_object *object, zend_string *member, int type, void **cache_slot) {
zend_string *prop_name = member;
#endif
if ((type == BP_VAR_W || type == BP_VAR_RW || type == BP_VAR_UNSET)
&& zend_string_equals_literal(prop_name, "active")) {
return NULL; // prevent cache fill; read_property handles the copy
}
return zend_std_get_property_ptr_ptr(object, member, type, cache_slot);
}

#if PHP_VERSION_ID < 80000
#if PHP_VERSION_ID >= 70400
static zval *ddtrace_span_stack_readonly(zval *object, zval *member, zval *value, void **cache_slot) {
Expand Down Expand Up @@ -1328,6 +1359,8 @@ static void dd_register_span_data_ce(void) {
memcpy(&ddtrace_span_stack_handlers, &std_object_handlers, sizeof(zend_object_handlers));
ddtrace_span_stack_handlers.clone_obj = ddtrace_span_stack_clone_obj;
ddtrace_span_stack_handlers.dtor_obj = ddtrace_span_stack_dtor_obj;
ddtrace_span_stack_handlers.read_property = ddtrace_span_stack_read_property;
ddtrace_span_stack_handlers.get_property_ptr_ptr = ddtrace_span_stack_get_property_ptr_ptr;
ddtrace_span_stack_handlers.write_property = ddtrace_span_stack_readonly;

}
Expand Down
34 changes: 34 additions & 0 deletions tests/ext/span_stack/span_stack_active_reference.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
--TEST--
SpanStack active references do not corrupt child span opening
--DESCRIPTION--
DDTrace\SpanStack::$active is exposed as a normal PHP property, but the C span
stack also aliases the same zval slot as a raw ddtrace_span_properties pointer.
If userland acquires that property by reference, the slot becomes IS_REFERENCE.
Opening a later child span must not treat the zend_reference container as a
SpanData pointer while inheriting parent state.

This reproduces the parent-service corruption path where the next span can crash
while reading or copying properties from what should be the active parent span.
--ENV--
DD_TRACE_AUTO_FLUSH_ENABLED=0
DD_TRACE_GENERATE_ROOT_SPAN=0
DD_CODE_ORIGIN_FOR_SPANS_ENABLED=0
--FILE--
<?php

function touch_ref(&$value) {}

$root = \DDTrace\start_span();
$root->service = "parent-service";
touch_ref(\DDTrace\active_stack()->active);
$child = \DDTrace\start_span();

var_dump($child->parent === $root);
var_dump($child->service);
echo "ok\n";

?>
--EXPECT--
bool(true)
string(14) "parent-service"
ok
Loading