Skip to content

Commit b5df6e7

Browse files
committed
Simplify sandboxed function calling
Signed-off-by: Bob Weinand <[email protected]>
1 parent 66951c5 commit b5df6e7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+357
-2904
lines changed

config.m4

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -229,8 +229,7 @@ if test "$PHP_DDTRACE" != "no"; then
229229
zend_abstract_interface/headers/headers.c \
230230
zend_abstract_interface/hook/hook.c \
231231
zend_abstract_interface/json/json.c \
232-
zend_abstract_interface/symbols/lookup.c \
233-
zend_abstract_interface/symbols/call.c \
232+
zend_abstract_interface/sandbox/call.c \
234233
zend_abstract_interface/uri_normalization/uri_normalization.c \
235234
zend_abstract_interface/zai_string/string.c \
236235
"
@@ -294,7 +293,6 @@ EOT
294293

295294
PHP_ADD_INCLUDE([$ext_srcdir/zend_abstract_interface])
296295
PHP_ADD_BUILD_DIR([$ext_builddir/zend_abstract_interface])
297-
PHP_ADD_BUILD_DIR([$ext_builddir/zend_abstract_interface/symbols])
298296
PHP_ADD_BUILD_DIR([$ext_builddir/zend_abstract_interface/config])
299297
PHP_ADD_BUILD_DIR([$ext_builddir/zend_abstract_interface/env])
300298
PHP_ADD_BUILD_DIR([$ext_builddir/zend_abstract_interface/exceptions])

config.w32

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,8 @@ if (PHP_DDTRACE != 'no') {
104104
ADD_SOURCES(zai_dirname + "/headers", "headers.c", "ddtrace");
105105
ADD_SOURCES(zai_dirname + "/hook", "hook.c", "ddtrace");
106106
ADD_SOURCES(zai_dirname + "/json", "json.c", "ddtrace");
107+
ADD_SOURCES(zai_dirname + "/sandbox", "call.c", "ddtrace");
107108
ADD_SOURCES(zai_dirname + "/zai_string", "string.c", "ddtrace");
108-
ADD_SOURCES(zai_dirname + "/symbols", "call.c lookup.c", "ddtrace");
109109
ADD_SOURCES(zai_dirname + "/uri_normalization", "uri_normalization.c", "ddtrace");
110110
if (version < 800) {
111111
ADD_SOURCES(zai_dirname + "/sandbox/php7", "sandbox.c", "ddtrace");

ext/compatibility.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,29 @@
6363
#define ZVAL_VARARG_PARAM(list, arg_num) (&(((zval *)list)[arg_num]))
6464
#define IS_TRUE_P(x) (Z_TYPE_P(x) == IS_TRUE)
6565

66+
static zend_always_inline zend_fcall_info dd_fcall_info(int argc, zval *args, zval *rv) {
67+
return (zend_fcall_info){
68+
.param_count = argc,
69+
.params = args,
70+
.retval = rv,
71+
.size = sizeof(zend_fcall_info),
72+
#if PHP_VERSION_ID < 80000
73+
.initialized = 1,
74+
#endif
75+
#if PHP_VERSION_ID >= 80000
76+
.named_params = NULL,
77+
#endif
78+
};
79+
}
80+
81+
static zend_always_inline void dd_get_closure_to_fcc(zval *closure, zend_fcall_info_cache *fcc) {
82+
#if PHP_VERSION_ID < 80000
83+
Z_OBJ_HANDLER_P(closure, get_closure)(closure, &fcc->called_scope, &fcc->function_handler, &fcc->object, 1);
84+
#else
85+
Z_OBJ_HANDLER_P(closure, get_closure)(Z_OBJ_P(closure), &fcc->called_scope, &fcc->function_handler, &fcc->object, 1);
86+
#endif
87+
}
88+
6689
static inline zval *ddtrace_assign_variable(zval *variable_ptr, zval *value) {
6790
#if PHP_VERSION_ID < 70400
6891
return zend_assign_to_variable(variable_ptr, value, IS_TMP_VAR);
@@ -424,6 +447,15 @@ static zend_always_inline void zend_array_release(zend_array *array)
424447
}
425448
}
426449

450+
static inline void *zend_hash_find_ptr_lc(const HashTable *ht, zend_string *key) {
451+
void *result;
452+
zend_string *lc_key = zend_string_tolower(key);
453+
result = zend_hash_find_ptr(ht, lc_key);
454+
zend_string_release(lc_key);
455+
return result;
456+
}
457+
458+
427459
#define ZEND_UNREACHABLE() ZEND_ASSUME(0)
428460

429461
#define ZEND_ARG_SEND_MODE(arg_info) (arg_info)->pass_by_reference

ext/exception_serialize.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -411,13 +411,12 @@ static void ddtrace_collect_exception_debug_data(zend_object *exception, zend_st
411411
ddog_CharSlice class_slice = DDOG_CHARSLICE_C("");
412412
zval *class_name = zend_hash_find(Z_ARR_P(frame), ZSTR_KNOWN(ZEND_STR_CLASS));
413413
if (class_name && Z_TYPE_P(class_name) == IS_STRING) {
414-
ce = zai_symbol_lookup_class_global(zai_str_from_zstr(Z_STR_P(class_name)));
414+
ce = zend_hash_find_ptr_lc(EG(class_table), Z_STR_P(class_name));
415415
class_slice = dd_zend_string_to_CharSlice(Z_STR_P(class_name));
416416
}
417417
zval *func_name = zend_hash_find(Z_ARR_P(frame), ZSTR_KNOWN(ZEND_STR_FUNCTION));
418418
if (func_name && Z_TYPE_P(func_name) == IS_STRING) {
419-
zai_str wtf = zai_str_from_zstr(Z_STR_P(func_name));
420-
func = zai_symbol_lookup_function(ce ? ZAI_SYMBOL_SCOPE_CLASS : ZAI_SYMBOL_SCOPE_GLOBAL, ce, &wtf);
419+
func = zend_hash_find_ptr_lc(ce ? &ce->function_table : EG(function_table), Z_STR_P(func_name));
421420
func_slice = dd_zend_string_to_CharSlice(Z_STR_P(func_name));
422421
}
423422

ext/hook/uhook.c

Lines changed: 53 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ typedef struct {
3737
} dd_closure_list;
3838

3939
typedef struct {
40-
zend_object *begin;
41-
zend_object *end;
40+
dd_uhook_callback begin;
41+
dd_uhook_callback end;
4242
bool running;
4343
zend_long id;
4444

@@ -75,6 +75,33 @@ typedef struct {
7575
dd_hook_data *hook_data;
7676
} dd_uhook_dynamic;
7777

78+
void dd_uhook_callback_apply_scope(dd_uhook_callback *cb, zend_class_entry *scope) {
79+
zend_function *func = (zend_function *)zend_get_closure_method_def(cb->closure);
80+
if (!cb->fcc.function_handler) {
81+
cb->is_static = func->common.fn_flags & ZEND_ACC_STATIC;
82+
if (cb->is_static) {
83+
goto end;
84+
}
85+
}
86+
if (GC_REFCOUNT(cb->closure) == 1) {
87+
func->common.scope = scope;
88+
if (!(func->common.fn_flags & ZEND_ACC_HEAP_RT_CACHE)) {
89+
func->op_array.fn_flags |= ZEND_ACC_HEAP_RT_CACHE;
90+
ZEND_MAP_PTR_INIT(func->op_array.run_time_cache, emalloc(func->op_array.cache_size));
91+
}
92+
} else {
93+
zval zv;
94+
zend_create_closure(&zv, func, scope, scope, NULL);
95+
OBJ_RELEASE(cb->closure);
96+
cb->closure = Z_OBJ(zv);
97+
func = (zend_function *)zend_get_closure_method_def(cb->closure);
98+
}
99+
memset(ZEND_MAP_PTR_GET(func->op_array.run_time_cache), 0, func->op_array.cache_size);
100+
end:
101+
cb->fcc.function_handler = func;
102+
cb->fcc.called_scope = func->common.scope;
103+
}
104+
78105
static zend_object *dd_hook_data_create(zend_class_entry *class_type) {
79106
dd_hook_data *hook_data = ecalloc(1, sizeof(*hook_data));
80107
zend_object_std_init(&hook_data->std, class_type);
@@ -192,20 +219,18 @@ void dd_uhook_report_sandbox_error(zend_execute_data *execute_data, zend_object
192219
})
193220
}
194221

195-
static bool dd_uhook_call_hook(zend_execute_data *execute_data, zend_object *closure, dd_hook_data *hook_data) {
196-
zval closure_zv, hook_data_zv;
197-
ZVAL_OBJ(&closure_zv, closure);
222+
static bool dd_uhook_call_hook(zend_execute_data *execute_data, dd_uhook_callback *callback, dd_hook_data *hook_data) {
223+
zval hook_data_zv;
198224
ZVAL_OBJ(&hook_data_zv, &hook_data->std);
199225

200-
bool has_this = getThis() != NULL;
201226
zval rv;
202227
zai_sandbox sandbox;
203228
zai_sandbox_open(&sandbox);
204-
bool success = zai_symbol_call(has_this ? ZAI_SYMBOL_SCOPE_OBJECT : ZAI_SYMBOL_SCOPE_GLOBAL, has_this ? &EX(This) : NULL,
205-
ZAI_SYMBOL_FUNCTION_CLOSURE, &closure_zv,
206-
&rv, 1 | ZAI_SYMBOL_SANDBOX, &sandbox, &hook_data_zv);
229+
dd_uhook_callback_ensure_scope(callback, execute_data);
230+
zend_fcall_info fci = dd_fcall_info(1, &hook_data_zv, &rv);
231+
bool success = zai_sandbox_call(&sandbox, &fci, &callback->fcc);
207232
if (!success || PG(last_error_message)) {
208-
dd_uhook_report_sandbox_error(execute_data, closure);
233+
dd_uhook_report_sandbox_error(execute_data, callback->closure);
209234
}
210235
zai_sandbox_close(&sandbox);
211236
zval_ptr_dtor(&rv);
@@ -298,7 +323,7 @@ static bool dd_uhook_begin(zend_ulong invocation, zend_execute_data *execute_dat
298323
ZVAL_ARR(&dyn->hook_data->property_args, dd_uhook_collect_args(execute_data));
299324
}
300325

301-
if (def->begin && !def->running) {
326+
if (def->begin.closure && !def->running) {
302327
dyn->hook_data->execute_data = execute_data;
303328
// We support it for PHP 8 for now, given we need this for PHP 8.1+ right now.
304329
// Bringing it to PHP 7.1-7.4 is possible, but not done yet.
@@ -310,10 +335,10 @@ static bool dd_uhook_begin(zend_ulong invocation, zend_execute_data *execute_dat
310335
}
311336
#endif
312337

313-
LOGEV(HOOK_TRACE, dd_uhook_log_invocation(log, execute_data, "begin", def->begin););
338+
LOGEV(HOOK_TRACE, dd_uhook_log_invocation(log, execute_data, "begin", def->begin.closure););
314339

315340
def->running = true;
316-
dd_uhook_call_hook(execute_data, def->begin, dyn->hook_data);
341+
dd_uhook_call_hook(execute_data, &def->begin, dyn->hook_data);
317342
def->running = false;
318343
dyn->hook_data->retval_ptr = NULL;
319344
}
@@ -395,7 +420,7 @@ static void dd_uhook_end(zend_ulong invocation, zend_execute_data *execute_data,
395420

396421
bool keep_span = true;
397422

398-
if (def->end && !def->running && get_DD_TRACE_ENABLED()) {
423+
if (def->end.closure && !def->running && get_DD_TRACE_ENABLED()) {
399424
zval tmp;
400425

401426
/* If the profiler doesn't handle a potential pending interrupt before
@@ -432,11 +457,11 @@ static void dd_uhook_end(zend_ulong invocation, zend_execute_data *execute_data,
432457
}
433458
zval_ptr_dtor(&tmp);
434459

435-
LOGEV(HOOK_TRACE, dd_uhook_log_invocation(log, execute_data, "end", def->end););
460+
LOGEV(HOOK_TRACE, dd_uhook_log_invocation(log, execute_data, "end", def->end.closure););
436461

437462
def->running = true;
438463
dyn->hook_data->retval_ptr = retval;
439-
keep_span = dd_uhook_call_hook(execute_data, def->end, dyn->hook_data);
464+
keep_span = dd_uhook_call_hook(execute_data, &def->end, dyn->hook_data);
440465
dyn->hook_data->retval_ptr = NULL;
441466
def->running = false;
442467
}
@@ -485,11 +510,11 @@ static void dd_uhook_end(zend_ulong invocation, zend_execute_data *execute_data,
485510

486511
static void dd_uhook_dtor(void *data) {
487512
dd_uhook_def *def = data;
488-
if (def->begin) {
489-
OBJ_RELEASE(def->begin);
513+
if (def->begin.closure) {
514+
OBJ_RELEASE(def->begin.closure);
490515
}
491-
if (def->end) {
492-
OBJ_RELEASE(def->end);
516+
if (def->end.closure) {
517+
OBJ_RELEASE(def->end.closure);
493518
}
494519
if (def->function) {
495520
zend_string_release(def->function);
@@ -581,13 +606,15 @@ PHP_FUNCTION(DDTrace_install_hook) {
581606
dd_uhook_def *def = emalloc(sizeof(*def));
582607
def->closure = NULL;
583608
def->running = false;
584-
def->begin = begin ? Z_OBJ_P(begin) : NULL;
585-
if (def->begin) {
586-
GC_ADDREF(def->begin);
609+
def->begin.fcc.function_handler = NULL;
610+
def->begin.closure = begin ? Z_OBJ_P(begin) : NULL;
611+
if (def->begin.closure) {
612+
GC_ADDREF(def->begin.closure);
587613
}
588-
def->end = end ? Z_OBJ_P(end) : NULL;
589-
if (def->end) {
590-
GC_ADDREF(def->end);
614+
def->end.fcc.function_handler = NULL;
615+
def->end.closure = end ? Z_OBJ_P(end) : NULL;
616+
if (def->end.closure) {
617+
GC_ADDREF(def->end.closure);
591618
}
592619
def->id = -1;
593620

ext/hook/uhook.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@
44
#include <php.h>
55
#include <sandbox/sandbox.h>
66

7+
typedef struct {
8+
zend_object *closure;
9+
zend_fcall_info_cache fcc;
10+
bool is_static;
11+
} dd_uhook_callback;
12+
713
HashTable *dd_uhook_collect_args(zend_execute_data *execute_data);
814
void dd_uhook_report_sandbox_error(zend_execute_data *execute_data, zend_object *closure);
915
void dd_uhook_log_invocation(void (*log)(const char *, ...), zend_execute_data *execute_data, const char *type, zend_object *closure);
@@ -14,6 +20,27 @@ void zai_uhook_rshutdown();
1420
void zai_uhook_minit(int module_number);
1521
void zai_uhook_mshutdown();
1622

23+
void dd_uhook_callback_apply_scope(dd_uhook_callback *cb, zend_class_entry *scope);
24+
static inline void dd_uhook_callback_ensure_scope(dd_uhook_callback *cb, zend_execute_data *execute_data) {
25+
zend_class_entry *scope;
26+
if (!cb->fcc.function_handler) {
27+
scope = zend_get_called_scope(execute_data);
28+
goto apply_scope;
29+
} else if (!cb->is_static) {
30+
bool has_this;
31+
scope = zend_get_called_scope(execute_data);
32+
if (scope != cb->fcc.called_scope) {
33+
apply_scope:
34+
dd_uhook_callback_apply_scope(cb, scope);
35+
has_this = !cb->is_static && getThis() != NULL;
36+
} else {
37+
has_this = getThis() != NULL;
38+
}
39+
40+
cb->fcc.object = has_this ? Z_OBJ(EX(This)) : NULL;
41+
}
42+
}
43+
1744
PHP_FUNCTION(DDTrace_trace_function);
1845
PHP_FUNCTION(DDTrace_trace_method);
1946
PHP_FUNCTION(DDTrace_hook_function);

0 commit comments

Comments
 (0)