diff --git a/ext/hook/uhook.c b/ext/hook/uhook.c index a9e6151aeae..e4db2721f7f 100644 --- a/ext/hook/uhook.c +++ b/ext/hook/uhook.c @@ -74,6 +74,7 @@ typedef struct { typedef struct { dd_hook_data *hook_data; + zend_class_entry *called_scope; } dd_uhook_dynamic; #if PHP_VERSION_ID < 70400 @@ -234,14 +235,14 @@ void dd_uhook_report_sandbox_error(zend_execute_data *execute_data, zend_object }) } -static bool dd_uhook_call_hook(zend_execute_data *execute_data, dd_uhook_callback *callback, dd_hook_data *hook_data) { +static bool dd_uhook_call_hook(zend_execute_data *execute_data, dd_uhook_callback *callback, dd_hook_data *hook_data, zend_class_entry *scope) { zval hook_data_zv; ZVAL_OBJ(&hook_data_zv, &hook_data->std); zval rv; zai_sandbox sandbox; zai_sandbox_open(&sandbox); - dd_uhook_callback_ensure_scope(callback, execute_data); + dd_uhook_callback_ensure_scope(callback, execute_data, scope); zend_fcall_info fci = dd_fcall_info(1, &hook_data_zv, &rv); bool success = zai_sandbox_call(&sandbox, &fci, &callback->fcc); if (!success || PG(last_error_message)) { @@ -321,6 +322,7 @@ static bool dd_uhook_begin(zend_ulong invocation, zend_execute_data *execute_dat return true; } + dyn->called_scope = zend_get_called_scope(execute_data); dyn->hook_data = (dd_hook_data *)dd_hook_data_create(ddtrace_hook_data_ce); dyn->hook_data->returns_reference = execute_data->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE; dyn->hook_data->vm_stack_top = EG(vm_stack_top); @@ -356,7 +358,7 @@ static bool dd_uhook_begin(zend_ulong invocation, zend_execute_data *execute_dat LOGEV(HOOK_TRACE, dd_uhook_log_invocation(log, execute_data, "begin", def->begin.closure);); def->running = true; - dd_uhook_call_hook(execute_data, &def->begin, dyn->hook_data); + dd_uhook_call_hook(execute_data, &def->begin, dyn->hook_data, dyn->called_scope); def->running = false; dyn->hook_data->retval_ptr = NULL; } @@ -480,7 +482,7 @@ static void dd_uhook_end(zend_ulong invocation, zend_execute_data *execute_data, def->running = true; dyn->hook_data->retval_ptr = retval; dyn->hook_data->execute_data = execute_data; - keep_span = dd_uhook_call_hook(execute_data, &def->end, dyn->hook_data); + keep_span = dd_uhook_call_hook(execute_data, &def->end, dyn->hook_data, dyn->called_scope); dyn->hook_data->execute_data = NULL; dyn->hook_data->retval_ptr = NULL; def->running = false; diff --git a/ext/hook/uhook.h b/ext/hook/uhook.h index f459a8df87e..662d7816be5 100644 --- a/ext/hook/uhook.h +++ b/ext/hook/uhook.h @@ -22,14 +22,12 @@ void zai_uhook_minit(int module_number); void zai_uhook_mshutdown(); void dd_uhook_callback_apply_scope(dd_uhook_callback *cb, zend_class_entry *scope); -static inline void dd_uhook_callback_ensure_scope(dd_uhook_callback *cb, zend_execute_data *execute_data) { - zend_class_entry *scope; +// Note that we cannot access zend_get_called_scope(execute_data) here - we need to have it provided from earlier, it might have been invalidated by now, e.g. in ZEND_NAMED_FUNCTION(zend_closure_internal_handler). +static inline void dd_uhook_callback_ensure_scope(dd_uhook_callback *cb, zend_execute_data *execute_data, zend_class_entry *scope) { if (!cb->fcc.function_handler) { - scope = zend_get_called_scope(execute_data); goto apply_scope; } else if (!cb->is_static) { bool has_this; - scope = zend_get_called_scope(execute_data); if (scope != cb->fcc.called_scope) { apply_scope: dd_uhook_callback_apply_scope(cb, scope); diff --git a/ext/hook/uhook_legacy.c b/ext/hook/uhook_legacy.c index a59b3246cf0..074794ee9c4 100644 --- a/ext/hook/uhook_legacy.c +++ b/ext/hook/uhook_legacy.c @@ -24,6 +24,7 @@ typedef struct { typedef struct { zend_array *args; ddtrace_span_data *span; + zend_class_entry *called_scope; bool skipped; bool dropped_span; bool was_primed; @@ -35,7 +36,7 @@ static bool dd_uhook_call(dd_uhook_callback *callback, bool tracing, dd_uhook_dy #define ZVAL_EXCEPTION(zv) do { if (EG(exception)) ZVAL_OBJ(zv, EG(exception)); else ZVAL_NULL(zv); } while (0) if (tracing) { - dd_uhook_callback_ensure_scope(callback, execute_data); + dd_uhook_callback_ensure_scope(callback, execute_data, dyn->called_scope); ZVAL_OBJ(¶ms[0], &dyn->span->std); ZVAL_ARR(¶ms[1], dyn->args); @@ -62,9 +63,8 @@ static bool dd_uhook_call(dd_uhook_callback *callback, bool tracing, dd_uhook_dy ZVAL_COPY_VALUE(¶ms[0], This); callback->fcc.object = Z_OBJ_P(This); } - zend_class_entry *scope_ce = zend_get_called_scope(execute_data); - if (scope_ce) { - ZVAL_STR(¶ms[1], scope_ce->name); + if (dyn->called_scope) { + ZVAL_STR(¶ms[1], dyn->called_scope->name); } else { ZVAL_NULL(¶ms[1]); } @@ -108,6 +108,7 @@ static bool dd_uhook_begin(zend_ulong invocation, zend_execute_data *execute_dat dyn->skipped = false; dyn->was_primed = false; dyn->dropped_span = false; + dyn->called_scope = zend_get_called_scope(execute_data); dyn->args = dd_uhook_collect_args(execute_data); if (def->tracing) { diff --git a/tests/ext/sandbox/install_hook/internal_fake_closure_forced_parse_error_asan.phpt b/tests/ext/sandbox/install_hook/internal_fake_closure_forced_parse_error_asan.phpt new file mode 100644 index 00000000000..120d3ba83d1 --- /dev/null +++ b/tests/ext/sandbox/install_hook/internal_fake_closure_forced_parse_error_asan.phpt @@ -0,0 +1,35 @@ +--TEST-- +ASAN repro: internal fake closure + forced eval parse error +--SKIPIF-- + +--INI-- +datadog.trace.generate_root_span=0 +datadog.trace.auto_flush_enabled=0 +--FILE-- +getClosure(); + +\DDTrace\install_hook( + $closure, + null, + function () { + eval('throw new \\Exception("boom");'); + }, + \DDTrace\HOOK_INSTANCE +); + +try { + $closure(1); +} catch (Throwable $e) { + // ignore +} + +echo "ok\n"; +?> +--EXPECT-- +ok +