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
1 change: 1 addition & 0 deletions ext/compatibility.h
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ static inline HashTable *zend_new_array(uint32_t nSize) {
#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
#define zend_std_unset_property std_object_handlers.unset_property

// make ZEND_STRL work
#undef zend_hash_str_update
Expand Down
46 changes: 46 additions & 0 deletions tests/ext/span_stack/span_stack_active_unset_child.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
--TEST--
SpanStack active unset does not corrupt child span opening
--DESCRIPTION--
DDTrace\SpanStack::$active aliases a C raw span pointer. Unsetting it must be
rejected instead of dropping the active span zval while leaving a stale internal
parent pointer behind.
--ENV--
DD_TRACE_AUTO_FLUSH_ENABLED=0
DD_TRACE_GENERATE_ROOT_SPAN=0
DD_CODE_ORIGIN_FOR_SPANS_ENABLED=0
--FILE--
<?php

use DDTrace\SpanData;

function span_stack_active_unset_child() {}

\DDTrace\trace_function('span_stack_active_unset_child', static function (SpanData $span) {
$span->name = 'span_stack_active_unset_child';
});

$root = \DDTrace\start_span();
$root->service = 'parent-service';
$stack = \DDTrace\active_stack();

try {
unset($stack->active);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

unset($root);
gc_collect_cycles();

$junk = [];
for ($i = 0; $i < 10000; $i++) {
$junk[] = new stdClass();
}

span_stack_active_unset_child();
echo "ok\n";

?>
--EXPECT--
Cannot unset readonly property DDTrace\SpanStack::$active
ok
35 changes: 31 additions & 4 deletions tracer/functions.c
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,11 @@ static zval *ddtrace_root_span_data_write(zend_object *object, zend_string *memb
#endif
}

static bool ddtrace_span_stack_is_context_property(zend_string *prop_name) {
return zend_string_equals_literal(prop_name, "active")
|| zend_string_equals_literal(prop_name, "parent");
}

#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();
Expand All @@ -595,8 +600,12 @@ static zval *ddtrace_span_stack_read_property(zend_object *object, zend_string *
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);
&& ddtrace_span_stack_is_context_property(prop_name)) {
if (zend_string_equals_literal(prop_name, "active")) {
ZVAL_COPY(rv, &stack->property_active);
} else {
ZVAL_COPY(rv, &stack->property_parent);
}
return rv;
}
return zend_std_read_property(object, member, type, cache_slot, rv);
Expand All @@ -610,12 +619,29 @@ static zval *ddtrace_span_stack_get_property_ptr_ptr(zend_object *object, zend_s
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")) {
&& ddtrace_span_stack_is_context_property(prop_name)) {
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
static void ddtrace_span_stack_unset_property(zval *object, zval *member, void **cache_slot) {
zend_object *obj = Z_OBJ_P(object);
zend_string *prop_name = Z_TYPE_P(member) == IS_STRING ? Z_STR_P(member) : ZSTR_EMPTY_ALLOC();
#else
static void ddtrace_span_stack_unset_property(zend_object *object, zend_string *member, void **cache_slot) {
zend_object *obj = object;
zend_string *prop_name = member;
#endif
if (ddtrace_span_stack_is_context_property(prop_name)) {
zend_throw_error(zend_ce_error, "Cannot unset readonly property %s::$%s", ZSTR_VAL(obj->ce->name), ZSTR_VAL(prop_name));
return;
}

zend_std_unset_property(object, member, 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 All @@ -629,7 +655,7 @@ static zval *ddtrace_span_stack_readonly(zend_object *object, zend_string *membe
zend_object *obj = object;
zend_string *prop_name = member;
#endif
if (zend_string_equals_literal(prop_name, "parent")) {
if (ddtrace_span_stack_is_context_property(prop_name)) {
zend_throw_error(zend_ce_error, "Cannot modify readonly property %s::$%s", ZSTR_VAL(obj->ce->name), ZSTR_VAL(prop_name));
#if PHP_VERSION_ID >= 70400
return &EG(uninitialized_zval);
Expand Down Expand Up @@ -722,6 +748,7 @@ static void dd_register_span_data_ce(void) {
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.unset_property = ddtrace_span_stack_unset_property;
ddtrace_span_stack_handlers.write_property = ddtrace_span_stack_readonly;

}
Expand Down
Loading