Skip to content
Closed
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
26 changes: 0 additions & 26 deletions Zend/tests/bug50383.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -58,19 +58,6 @@ Array
)

[1] => Array
(
[file] => %s
[line] => 13
[function] => ThrowException
[class] => myClass
[type] => ::
[args] => Array
(
)

)

[2] => Array
(
[file] => %s
[line] => 21
Expand Down Expand Up @@ -104,19 +91,6 @@ Array
)

[1] => Array
(
[file] => %s
[line] => 17
[function] => foo
[class] => myClass
[type] => ->
[args] => Array
(
)

)

[2] => Array
(
[file] => %s
[line] => 28
Expand Down
19 changes: 19 additions & 0 deletions Zend/tests/bug68412.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
--TEST--
Bug #68412 (Infinite recursion with __call can make the program crash/segfault)
--FILE--
<?php
class C {
public function __call($x, $y) {
global $z;
$z->bar();
}
}
$z = new C;
function main() {
global $z;
$z->foo();
}
main();
?>
--EXPECTF--
Fatal error: Allowed memory size of %d bytes exhausted%s(tried to allocate %d bytes) in %sbug68412.php on line %d
14 changes: 14 additions & 0 deletions Zend/zend.c
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,17 @@ static void zend_init_exception_op(void) /* {{{ */
}
/* }}} */

static void zend_init_call_trampoline_op(void) /* {{{ */
{
memset(&EG(call_trampoline_op), 0, sizeof(EG(call_trampoline_op)));
EG(call_trampoline_op).opcode = ZEND_CALL_TRAMPOLINE;
EG(call_trampoline_op).op1_type = IS_UNUSED;
EG(call_trampoline_op).op2_type = IS_UNUSED;
EG(call_trampoline_op).result_type = IS_UNUSED;
ZEND_VM_SET_OPCODE_HANDLER(&EG(call_trampoline_op));
}
/* }}} */

#ifdef ZTS
static void function_copy_ctor(zval *zv)
{
Expand Down Expand Up @@ -511,6 +522,8 @@ static void executor_globals_ctor(zend_executor_globals *executor_globals) /* {{
zend_copy_constants(EG(zend_constants), GLOBAL_CONSTANTS_TABLE);
zend_init_rsrc_plist();
zend_init_exception_op();
zend_init_call_trampoline_op();
memset(&executor_globals->trampoline, 0, sizeof(zend_op_array));
executor_globals->lambda_count = 0;
ZVAL_UNDEF(&executor_globals->user_error_handler);
ZVAL_UNDEF(&executor_globals->user_exception_handler);
Expand Down Expand Up @@ -722,6 +735,7 @@ int zend_startup(zend_utility_functions *utility_functions, char **extensions) /
#ifndef ZTS
zend_init_rsrc_plist();
zend_init_exception_op();
zend_init_call_trampoline_op();
#endif

zend_ini_startup();
Expand Down
34 changes: 11 additions & 23 deletions Zend/zend_API.c
Original file line number Diff line number Diff line change
Expand Up @@ -3070,16 +3070,7 @@ static int zend_is_callable_check_func(int check_flags, zval *callable, zend_fca
get_function_via_handler:
if (fcc->object && fcc->calling_scope == ce_org) {
if (strict_class && ce_org->__call) {
fcc->function_handler = emalloc(sizeof(zend_internal_function));
fcc->function_handler->internal_function.type = ZEND_INTERNAL_FUNCTION;
fcc->function_handler->internal_function.module = (ce_org->type == ZEND_INTERNAL_CLASS) ? ce_org->info.internal.module : NULL;
fcc->function_handler->internal_function.handler = zend_std_call_user_call;
fcc->function_handler->internal_function.arg_info = NULL;
fcc->function_handler->internal_function.num_args = 0;
fcc->function_handler->internal_function.scope = ce_org;
fcc->function_handler->internal_function.fn_flags = ZEND_ACC_CALL_VIA_HANDLER;
fcc->function_handler->internal_function.function_name = mname;
zend_string_addref(mname);
fcc->function_handler = zend_get_call_trampoline_func(ce_org, mname, 0);
call_via_handler = 1;
retval = 1;
} else if (fcc->object->handlers->get_method) {
Expand All @@ -3088,15 +3079,15 @@ static int zend_is_callable_check_func(int check_flags, zval *callable, zend_fca
if (strict_class &&
(!fcc->function_handler->common.scope ||
!instanceof_function(ce_org, fcc->function_handler->common.scope))) {
if ((fcc->function_handler->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) != 0) {
if (fcc->function_handler->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) {
if (fcc->function_handler->type != ZEND_OVERLOADED_FUNCTION) {
zend_string_release(fcc->function_handler->common.function_name);
}
efree(fcc->function_handler);
zend_free_trampoline(fcc->function_handler);
}
} else {
retval = 1;
call_via_handler = (fcc->function_handler->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) != 0;
call_via_handler = (fcc->function_handler->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) != 0;
}
}
}
Expand All @@ -3108,7 +3099,7 @@ static int zend_is_callable_check_func(int check_flags, zval *callable, zend_fca
}
if (fcc->function_handler) {
retval = 1;
call_via_handler = (fcc->function_handler->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) != 0;
call_via_handler = (fcc->function_handler->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) != 0;
if (call_via_handler && !fcc->object && EG(current_execute_data) && Z_OBJ(EG(current_execute_data)->This) &&
instanceof_function(Z_OBJCE(EG(current_execute_data)->This), fcc->calling_scope)) {
fcc->object = Z_OBJ(EG(current_execute_data)->This);
Expand Down Expand Up @@ -3250,14 +3241,13 @@ ZEND_API zend_bool zend_is_callable_ex(zval *callable, zend_object *object, uint
ret = zend_is_callable_check_func(check_flags, callable, fcc, 0, error);
if (fcc == &fcc_local &&
fcc->function_handler &&
((fcc->function_handler->type == ZEND_INTERNAL_FUNCTION &&
(fcc->function_handler->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER)) ||
((fcc->function_handler->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) ||
fcc->function_handler->type == ZEND_OVERLOADED_FUNCTION_TEMPORARY ||
fcc->function_handler->type == ZEND_OVERLOADED_FUNCTION)) {
if (fcc->function_handler->type != ZEND_OVERLOADED_FUNCTION) {
zend_string_release(fcc->function_handler->common.function_name);
}
efree(fcc->function_handler);
zend_free_trampoline(fcc->function_handler);
}
return ret;

Expand Down Expand Up @@ -3338,14 +3328,13 @@ ZEND_API zend_bool zend_is_callable_ex(zval *callable, zend_object *object, uint
ret = zend_is_callable_check_func(check_flags, method, fcc, strict_class, error);
if (fcc == &fcc_local &&
fcc->function_handler &&
((fcc->function_handler->type == ZEND_INTERNAL_FUNCTION &&
(fcc->function_handler->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER)) ||
((fcc->function_handler->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) ||
fcc->function_handler->type == ZEND_OVERLOADED_FUNCTION_TEMPORARY ||
fcc->function_handler->type == ZEND_OVERLOADED_FUNCTION)) {
if (fcc->function_handler->type != ZEND_OVERLOADED_FUNCTION) {
zend_string_release(fcc->function_handler->common.function_name);
}
efree(fcc->function_handler);
zend_free_trampoline(fcc->function_handler);
}
return ret;

Expand Down Expand Up @@ -3414,14 +3403,13 @@ ZEND_API zend_bool zend_make_callable(zval *callable, zend_string **callable_nam
add_next_index_str(callable, zend_string_copy(fcc.function_handler->common.function_name));
}
if (fcc.function_handler &&
((fcc.function_handler->type == ZEND_INTERNAL_FUNCTION &&
(fcc.function_handler->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER)) ||
((fcc.function_handler->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) ||
fcc.function_handler->type == ZEND_OVERLOADED_FUNCTION_TEMPORARY ||
fcc.function_handler->type == ZEND_OVERLOADED_FUNCTION)) {
if (fcc.function_handler->type != ZEND_OVERLOADED_FUNCTION) {
zend_string_release(fcc.function_handler->common.function_name);
}
efree(fcc.function_handler);
zend_free_trampoline(fcc.function_handler);
}
return 1;
}
Expand Down
9 changes: 3 additions & 6 deletions Zend/zend_builtin_functions.c
Original file line number Diff line number Diff line change
Expand Up @@ -1285,16 +1285,14 @@ ZEND_FUNCTION(method_exists)
&& Z_OBJ_HT_P(klass)->get_method != NULL
&& (func = Z_OBJ_HT_P(klass)->get_method(&Z_OBJ_P(klass), method_name, NULL)) != NULL
) {
if (func->type == ZEND_INTERNAL_FUNCTION
&& (func->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) != 0
) {
if (func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) {
/* Returns true to the fake Closure's __invoke */
RETVAL_BOOL(func->common.scope == zend_ce_closure
&& zend_string_equals_literal(method_name, ZEND_INVOKE_FUNC_NAME));

zend_string_release(lcname);
zend_string_release(func->common.function_name);
efree(func);
zend_free_trampoline(func);
return;
}
zend_string_release(lcname);
Expand Down Expand Up @@ -2508,8 +2506,7 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int
if (prev_call &&
prev_call->func &&
!ZEND_USER_CODE(prev_call->func->common.type) &&
!(prev_call->func->common.type == ZEND_INTERNAL_FUNCTION &&
(prev_call->func->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER))) {
!(prev_call->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) {
break;
}
if (prev->func && ZEND_USER_CODE(prev->func->common.type)) {
Expand Down
7 changes: 5 additions & 2 deletions Zend/zend_compile.h
Original file line number Diff line number Diff line change
Expand Up @@ -226,8 +226,11 @@ typedef struct _zend_try_catch_element {
#define ZEND_ACC_CLOSURE 0x100000
#define ZEND_ACC_GENERATOR 0x800000

/* function flag for internal user call handlers __call, __callstatic */
#define ZEND_ACC_CALL_VIA_HANDLER 0x200000
/* call through user function trampoline. e.g. __call, __callstatic */
#define ZEND_ACC_CALL_VIA_TRAMPOLINE 0x200000

/* call through internal function handler. e.g. Closure::invoke() */
#define ZEND_ACC_CALL_VIA_HANDLER ZEND_ACC_CALL_VIA_TRAMPOLINE

/* disable inline caching */
#define ZEND_ACC_NEVER_CACHE 0x400000
Expand Down
9 changes: 7 additions & 2 deletions Zend/zend_execute_API.c
Original file line number Diff line number Diff line change
Expand Up @@ -803,7 +803,7 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) /
Z_ADDREF_P(arg);
} else {
if (Z_ISREF_P(arg) &&
(func->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) == 0 ) {
!(func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) {
/* don't separate references for __call */
arg = Z_REFVAL_P(arg);
}
Expand All @@ -827,6 +827,7 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) /
}

if (func->type == ZEND_USER_FUNCTION) {
int call_via_handler = (func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) != 0;
EG(scope) = func->common.scope;
call->symbol_table = fci->symbol_table;
if (UNEXPECTED(func->op_array.fn_flags & ZEND_ACC_CLOSURE)) {
Expand All @@ -839,8 +840,12 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) /
} else {
zend_generator_create_zval(call, &func->op_array, fci->retval);
}
if (call_via_handler) {
/* We must re-initialize function again */
fci_cache->initialized = 0;
}
} else if (func->type == ZEND_INTERNAL_FUNCTION) {
int call_via_handler = (func->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) != 0;
int call_via_handler = (func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) != 0;
ZVAL_NULL(fci->retval);
if (func->common.scope) {
EG(scope) = func->common.scope;
Expand Down
4 changes: 4 additions & 0 deletions Zend/zend_globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include "zend_modules.h"
#include "zend_float.h"
#include "zend_multibyte.h"
#include "zend_multiply.h"
#include "zend_arena.h"

/* Define ZTS if you want a thread-safe Zend */
Expand Down Expand Up @@ -237,6 +238,9 @@ struct _zend_executor_globals {
XPFPA_CW_DATATYPE saved_fpu_cw;
#endif

zend_function trampoline;
zend_op call_trampoline_op;

void *reserved[ZEND_MAX_RESERVED_RESOURCES];
};

Expand Down
Loading