Browse Source

Opcode-fy magical __call

pull/1231/head
Xinchen Hui 11 years ago
parent
commit
73855f7d53
  1. 19
      Zend/tests/bug68412.phpt
  2. 4
      Zend/zend_builtin_functions.c
  3. 5
      Zend/zend_execute_API.c
  4. 53
      Zend/zend_object_handlers.c
  5. 44
      Zend/zend_vm_def.h
  6. 98
      Zend/zend_vm_execute.h
  7. 2
      Zend/zend_vm_opcodes.c
  8. 1
      Zend/zend_vm_opcodes.h

19
Zend/tests/bug68412.phpt

@ -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

4
Zend/zend_builtin_functions.c

@ -1285,9 +1285,7 @@ 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_HANDLER) != 0) {
/* 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));

5
Zend/zend_execute_API.c

@ -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_HANDLER) != 0;
EG(scope) = func->common.scope;
call->symbol_table = fci->symbol_table;
if (UNEXPECTED(func->op_array.fn_flags & ZEND_ACC_CLOSURE)) {
@ -839,6 +840,10 @@ 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;
ZVAL_NULL(fci->retval);

53
Zend/zend_object_handlers.c

@ -29,6 +29,7 @@
#include "zend_interfaces.h"
#include "zend_closures.h"
#include "zend_compile.h"
#include "zend_vm.h"
#include "zend_hash.h"
#define DEBUG_OBJECT_HANDLERS 0
@ -1036,23 +1037,45 @@ ZEND_API int zend_check_protected(zend_class_entry *ce, zend_class_entry *scope)
static inline union _zend_function *zend_get_user_call_function(zend_class_entry *ce, zend_string *method_name) /* {{{ */
{
zend_internal_function *call_user_call = emalloc(sizeof(zend_internal_function));
call_user_call->type = ZEND_INTERNAL_FUNCTION;
call_user_call->module = (ce->type == ZEND_INTERNAL_CLASS) ? ce->info.internal.module : NULL;
call_user_call->handler = zend_std_call_user_call;
call_user_call->arg_info = NULL;
call_user_call->num_args = 0;
call_user_call->scope = ce;
call_user_call->fn_flags = ZEND_ACC_CALL_VIA_HANDLER;
//??? keep compatibility for "\0" characters
//??? see: Zend/tests/bug46238.phpt
if (UNEXPECTED(strlen(method_name->val) != method_name->len)) {
call_user_call->function_name = zend_string_init(method_name->val, strlen(method_name->val), 0);
if (ce->type == ZEND_USER_CLASS) {
zend_op_array *call_user_call = ecalloc(1, ZEND_MM_ALIGNED_SIZE(sizeof(zend_op_array)) + sizeof(zend_op));
call_user_call->type = ZEND_USER_FUNCTION;
call_user_call->scope = ce;
call_user_call->prototype = ce->__call;
call_user_call->fn_flags = ZEND_ACC_CALL_VIA_HANDLER;
call_user_call->this_var = -1;
call_user_call->filename = ce->__call->op_array.filename;
call_user_call->opcodes = (zend_op *)((char *)call_user_call + ZEND_MM_ALIGNED_SIZE(sizeof(zend_op_array)));
call_user_call->opcodes[0].opcode = ZEND_PROXY_CALL;
call_user_call->opcodes[0].op1_type = IS_UNUSED;
call_user_call->opcodes[0].op2_type = IS_UNUSED;
call_user_call->opcodes[0].result_type = IS_UNUSED;
ZEND_VM_SET_OPCODE_HANDLER(&call_user_call->opcodes[0]);
if (UNEXPECTED(strlen(method_name->val) != method_name->len)) {
call_user_call->function_name = zend_string_init(method_name->val, strlen(method_name->val), 0);
} else {
call_user_call->function_name = zend_string_copy(method_name);
}
return (union _zend_function *)call_user_call;
} else {
call_user_call->function_name = zend_string_copy(method_name);
zend_internal_function *call_user_call = emalloc(sizeof(zend_internal_function));
call_user_call->type = ZEND_INTERNAL_FUNCTION;
call_user_call->module = (ce->type == ZEND_INTERNAL_CLASS) ? ce->info.internal.module : NULL;
call_user_call->handler = zend_std_call_user_call;
call_user_call->arg_info = NULL;
call_user_call->num_args = 0;
call_user_call->scope = ce;
call_user_call->fn_flags = ZEND_ACC_CALL_VIA_HANDLER;
//??? keep compatibility for "\0" characters
//??? see: Zend/tests/bug46238.phpt
if (UNEXPECTED(strlen(method_name->val) != method_name->len)) {
call_user_call->function_name = zend_string_init(method_name->val, strlen(method_name->val), 0);
} else {
call_user_call->function_name = zend_string_copy(method_name);
}
return (union _zend_function *)call_user_call;
}
return (union _zend_function *)call_user_call;
}
/* }}} */

44
Zend/zend_vm_def.h

@ -2376,6 +2376,7 @@ ZEND_VM_HELPER(zend_leave_helper, ANY, ANY)
}
OBJ_RELEASE(object);
}
EG(scope) = EX(func)->op_array.scope;
if (UNEXPECTED(EG(exception) != NULL)) {
@ -2417,7 +2418,7 @@ ZEND_VM_HELPER(zend_leave_helper, ANY, ANY)
EG(current_execute_data) = EX(prev_execute_data);
if (EX(func)->op_array.fn_flags & ZEND_ACC_CLOSURE) {
OBJ_RELEASE((zend_object*)EX(func)->op_array.prototype);
}
}
} else /* if (call_kind == ZEND_CALL_TOP_CODE) */ {
zend_array *symbol_table = EX(symbol_table);
@ -3768,8 +3769,8 @@ ZEND_VM_HANDLER(62, ZEND_RETURN, CONST|TMP|VAR|CV, ANY)
zend_free_op free_op1;
SAVE_OPLINE();
retval_ptr = GET_OP1_ZVAL_PTR(BP_VAR_R);
retval_ptr = GET_OP1_ZVAL_PTR(BP_VAR_R);
if (!EX(return_value)) {
FREE_OP1();
} else {
@ -7558,3 +7559,42 @@ ZEND_VM_HANDLER(157, ZEND_FETCH_CLASS_NAME, ANY, ANY)
ZEND_VM_NEXT_OPCODE();
}
ZEND_VM_HANDLER(158, ZEND_PROXY_CALL, ANY, ANY)
{
zval args;
zend_function *fbc = EX(func);
zend_object *obj = Z_OBJ(EX(This));
zval *return_value = EX(return_value);
zend_call_kind call_kind = EX_CALL_KIND();
uint32_t num_args = EX_NUM_ARGS();
zend_execute_data *call, *prev_execute_data = EX(prev_execute_data);
array_init_size(&args, num_args);
if (num_args) {
zval *p;
zend_hash_real_init(Z_ARRVAL(args), 1);
p = ZEND_CALL_ARG(execute_data, 1);
ZEND_HASH_FILL_PACKED(Z_ARRVAL(args)) {
uint32_t i;
for (i = 0; i < num_args; ++i) {
ZEND_HASH_FILL_ADD(p);
p++;
}
} ZEND_HASH_FILL_END();
}
zend_vm_stack_free_call_frame(execute_data);
call = zend_vm_stack_push_call_frame(call_kind,
fbc->common.prototype, 2, fbc->common.scope, obj, prev_execute_data);
ZVAL_STR(ZEND_CALL_ARG(call, 1), fbc->common.function_name);
ZVAL_COPY_VALUE(ZEND_CALL_ARG(call, 2), &args);
efree(fbc);
call->symbol_table = NULL;
i_init_func_execute_data(call, &call->func->op_array, return_value, 1);
ZEND_VM_ENTER();
}

98
Zend/zend_vm_execute.h

@ -466,6 +466,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_leave_helper_SPEC(ZEND_OPCODE_
}
OBJ_RELEASE(object);
}
EG(scope) = EX(func)->op_array.scope;
if (UNEXPECTED(EG(exception) != NULL)) {
@ -1772,6 +1773,45 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_NAME_SPEC_HANDLER(
ZEND_VM_NEXT_OPCODE();
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PROXY_CALL_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
zval args;
zend_function *fbc = EX(func);
zend_object *obj = Z_OBJ(EX(This));
zval *return_value = EX(return_value);
zend_call_kind call_kind = EX_CALL_KIND();
uint32_t num_args = EX_NUM_ARGS();
zend_execute_data *call, *prev_execute_data = EX(prev_execute_data);
array_init_size(&args, num_args);
if (num_args) {
zval *p;
zend_hash_real_init(Z_ARRVAL(args), 1);
p = ZEND_CALL_ARG(execute_data, 1);
ZEND_HASH_FILL_PACKED(Z_ARRVAL(args)) {
uint32_t i;
for (i = 0; i < num_args; ++i) {
ZEND_HASH_FILL_ADD(p);
p++;
}
} ZEND_HASH_FILL_END();
}
zend_vm_stack_free_call_frame(execute_data);
call = zend_vm_stack_push_call_frame(call_kind,
fbc->common.prototype, 2, fbc->common.scope, obj, prev_execute_data);
ZVAL_STR(ZEND_CALL_ARG(call, 1), fbc->common.function_name);
ZVAL_COPY_VALUE(ZEND_CALL_ARG(call, 2), &args);
efree(fbc);
call->symbol_table = NULL;
i_init_func_execute_data(call, &call->func->op_array, return_value, 1);
ZEND_VM_ENTER();
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@ -2813,8 +2853,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_SPEC_CONST_HANDLER(ZEND
SAVE_OPLINE();
retval_ptr = EX_CONSTANT(opline->op1);
retval_ptr = EX_CONSTANT(opline->op1);
if (!EX(return_value)) {
} else {
@ -10617,8 +10657,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_SPEC_TMP_HANDLER(ZEND_O
zend_free_op free_op1;
SAVE_OPLINE();
retval_ptr = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1);
retval_ptr = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1);
if (!EX(return_value)) {
zval_ptr_dtor_nogc(free_op1);
} else {
@ -13649,8 +13689,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_SPEC_VAR_HANDLER(ZEND_O
zend_free_op free_op1;
SAVE_OPLINE();
retval_ptr = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
retval_ptr = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
if (!EX(return_value)) {
zval_ptr_dtor_nogc(free_op1);
} else {
@ -27222,8 +27262,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_SPEC_CV_HANDLER(ZEND_OP
SAVE_OPLINE();
retval_ptr = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var);
retval_ptr = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var);
if (!EX(return_value)) {
} else {
@ -47346,31 +47386,31 @@ void zend_init_opcodes_handlers(void)
ZEND_FETCH_CLASS_NAME_SPEC_HANDLER,
ZEND_FETCH_CLASS_NAME_SPEC_HANDLER,
ZEND_FETCH_CLASS_NAME_SPEC_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_PROXY_CALL_SPEC_HANDLER,
ZEND_PROXY_CALL_SPEC_HANDLER,
ZEND_PROXY_CALL_SPEC_HANDLER,
ZEND_PROXY_CALL_SPEC_HANDLER,
ZEND_PROXY_CALL_SPEC_HANDLER,
ZEND_PROXY_CALL_SPEC_HANDLER,
ZEND_PROXY_CALL_SPEC_HANDLER,
ZEND_PROXY_CALL_SPEC_HANDLER,
ZEND_PROXY_CALL_SPEC_HANDLER,
ZEND_PROXY_CALL_SPEC_HANDLER,
ZEND_PROXY_CALL_SPEC_HANDLER,
ZEND_PROXY_CALL_SPEC_HANDLER,
ZEND_PROXY_CALL_SPEC_HANDLER,
ZEND_PROXY_CALL_SPEC_HANDLER,
ZEND_PROXY_CALL_SPEC_HANDLER,
ZEND_PROXY_CALL_SPEC_HANDLER,
ZEND_PROXY_CALL_SPEC_HANDLER,
ZEND_PROXY_CALL_SPEC_HANDLER,
ZEND_PROXY_CALL_SPEC_HANDLER,
ZEND_PROXY_CALL_SPEC_HANDLER,
ZEND_PROXY_CALL_SPEC_HANDLER,
ZEND_PROXY_CALL_SPEC_HANDLER,
ZEND_PROXY_CALL_SPEC_HANDLER,
ZEND_PROXY_CALL_SPEC_HANDLER,
ZEND_PROXY_CALL_SPEC_HANDLER,
ZEND_DISCARD_EXCEPTION_SPEC_HANDLER,
ZEND_DISCARD_EXCEPTION_SPEC_HANDLER,
ZEND_DISCARD_EXCEPTION_SPEC_HANDLER,

2
Zend/zend_vm_opcodes.c

@ -180,7 +180,7 @@ const char *zend_vm_opcodes_map[171] = {
"ZEND_BIND_TRAITS",
"ZEND_SEPARATE",
"ZEND_FETCH_CLASS_NAME",
NULL,
"ZEND_PROXY_CALL",
"ZEND_DISCARD_EXCEPTION",
"ZEND_YIELD",
"ZEND_GENERATOR_RETURN",

1
Zend/zend_vm_opcodes.h

@ -188,6 +188,7 @@ END_EXTERN_C()
#define ZEND_BIND_TRAITS 155
#define ZEND_SEPARATE 156
#define ZEND_FETCH_CLASS_NAME 157
#define ZEND_PROXY_CALL 158
#define ZEND_DISCARD_EXCEPTION 159
#define ZEND_YIELD 160
#define ZEND_GENERATOR_RETURN 161

Loading…
Cancel
Save