Skip to content

Commit 5e8133f

Browse files
committed
Squashed commit of the following:
commit 2399fc8 Author: Dmitry Stogov <[email protected]> Date: Fri Apr 10 12:38:08 2015 +0300 Removed useless assignment commit 796b633 Author: Dmitry Stogov <[email protected]> Date: Fri Apr 10 12:35:31 2015 +0300 Fixed execution with overriden zend_execute_ex() commit 4a9fb12 Author: Dmitry Stogov <[email protected]> Date: Fri Apr 10 02:02:58 2015 +0300 Fixed executor without global registers commit d456c30 Author: Dmitry Stogov <[email protected]> Date: Fri Apr 10 01:30:35 2015 +0300 Restored original behavior for tests/classes/__call_004.phpt commit 479646d Author: Dmitry Stogov <[email protected]> Date: Fri Apr 10 00:32:17 2015 +0300 Fixed test. We don't keep stack frame for fake function anymore. commit 9ae61e3 Author: Dmitry Stogov <[email protected]> Date: Fri Apr 10 00:30:09 2015 +0300 Use ZEND_ACC_CALL_VIA_TRAMPOLINE instead of ZEND_ACC_CALL_VIA_HANDLER. Keep ZEND_ACC_CALL_VIA_HANDLER for compatibility. commit 0a8403a Author: Dmitry Stogov <[email protected]> Date: Fri Apr 10 00:05:43 2015 +0300 Rename PROXY_CALL into CALL_TRAMPLINE. Generalize API to allow reuse EG(trampline) for other purposes. commit 4ea0525 Author: Dmitry Stogov <[email protected]> Date: Thu Apr 9 23:22:25 2015 +0300 Reuse EG(proxy_call_op) for all proxy. Move proxy related functions from zend_objects_API to zend_object_handlers. commit 529bf73 Author: Dmitry Stogov <[email protected]> Date: Thu Apr 9 21:42:23 2015 +0300 Accurate use of proxy_call commit 5d62837 Merge: 83e749f 690843f Author: Dmitry Stogov <[email protected]> Date: Thu Apr 9 19:40:00 2015 +0300 Merge branch 'master' into opcodefy-call * master: Fixed GOTO executor Fixed typo Changed ArrayIterator implementation using zend_hash_iterator_... API. Allowed modification of itterated ArrayObject using the same behavior as proposed in `Fix "foreach" behavior`. Removed "Array was modified outside object and internal position is no longer valid" hack. commit 83e749f Author: Dmitry Stogov <[email protected]> Date: Thu Apr 9 19:39:10 2015 +0300 Improved ZEND_PROXY_CALL commit 0c829af Author: Dmitry Stogov <[email protected]> Date: Thu Apr 9 15:14:49 2015 +0300 Reverted white-space changes commit df65144 Merge: 5fd2f97 97756d9 Author: Dmitry Stogov <[email protected]> Date: Thu Apr 9 14:37:07 2015 +0300 Merge branch 'opcodefy-call' of github.com:laruence/php-src into opcodefy-call * 'opcodefy-call' of github.com:laruence/php-src: Ready for PR Fixed static call Improve performance by using prealloated op_arrray Respect called_scope Support internal magical __call/__callStatic opcode-fy magical __callStatic Opcode-fy magical __call commit 97756d9 Author: Xinchen Hui <[email protected]> Date: Thu Apr 9 19:07:59 2015 +0800 Ready for PR commit 74f9930 Author: Xinchen Hui <[email protected]> Date: Thu Apr 9 19:03:00 2015 +0800 Fixed static call commit ec1d9eb Author: Xinchen Hui <[email protected]> Date: Thu Apr 9 18:23:17 2015 +0800 Improve performance by using prealloated op_arrray commit df7fbbf Author: Xinchen Hui <[email protected]> Date: Thu Apr 9 15:10:02 2015 +0800 Respect called_scope commit 769d1d5 Author: Xinchen Hui <[email protected]> Date: Thu Apr 9 12:19:23 2015 +0800 Support internal magical __call/__callStatic commit a980fed Author: Xinchen Hui <[email protected]> Date: Wed Apr 8 18:35:41 2015 +0800 opcode-fy magical __callStatic commit 73855f7 Author: Xinchen Hui <[email protected]> Date: Wed Apr 8 14:21:55 2015 +0800 Opcode-fy magical __call
1 parent 8eaa098 commit 5e8133f

16 files changed

+465
-242
lines changed

NEWS

+3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
. Update the MIME type list from the one shipped by Apache HTTPD. (Adam)
99

1010
- Core:
11+
. Improved __call() and __callStatic() magic method handling. Now they are
12+
called in a stackless way using ZEND_CALL_TRAMPOLINE opcode, without
13+
additional stack frame. (Laruence, Dmitry)
1114
. Fixed weird operators behavior. Division by zero now emits warning and
1215
returns +/-INF, modulo by zero and intdid() throws an exception, shifts
1316
by negative offset throw exceptions. Compile-time evaluation of division

Zend/tests/bug50383.phpt

-26
Original file line numberDiff line numberDiff line change
@@ -58,19 +58,6 @@ Array
5858
)
5959

6060
[1] => Array
61-
(
62-
[file] => %s
63-
[line] => 13
64-
[function] => ThrowException
65-
[class] => myClass
66-
[type] => ::
67-
[args] => Array
68-
(
69-
)
70-
71-
)
72-
73-
[2] => Array
7461
(
7562
[file] => %s
7663
[line] => 21
@@ -104,19 +91,6 @@ Array
10491
)
10592

10693
[1] => Array
107-
(
108-
[file] => %s
109-
[line] => 17
110-
[function] => foo
111-
[class] => myClass
112-
[type] => ->
113-
[args] => Array
114-
(
115-
)
116-
117-
)
118-
119-
[2] => Array
12094
(
12195
[file] => %s
12296
[line] => 28

Zend/tests/bug68412.phpt

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
--TEST--
2+
Bug #68412 (Infinite recursion with __call can make the program crash/segfault)
3+
--FILE--
4+
<?php
5+
class C {
6+
public function __call($x, $y) {
7+
global $z;
8+
$z->bar();
9+
}
10+
}
11+
$z = new C;
12+
function main() {
13+
global $z;
14+
$z->foo();
15+
}
16+
main();
17+
?>
18+
--EXPECTF--
19+
Fatal error: Allowed memory size of %d bytes exhausted%s(tried to allocate %d bytes) in %sbug68412.php on line %d

Zend/zend.c

+14
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,17 @@ static void zend_init_exception_op(void) /* {{{ */
432432
}
433433
/* }}} */
434434

435+
static void zend_init_call_trampoline_op(void) /* {{{ */
436+
{
437+
memset(&EG(call_trampoline_op), 0, sizeof(EG(call_trampoline_op)));
438+
EG(call_trampoline_op).opcode = ZEND_CALL_TRAMPOLINE;
439+
EG(call_trampoline_op).op1_type = IS_UNUSED;
440+
EG(call_trampoline_op).op2_type = IS_UNUSED;
441+
EG(call_trampoline_op).result_type = IS_UNUSED;
442+
ZEND_VM_SET_OPCODE_HANDLER(&EG(call_trampoline_op));
443+
}
444+
/* }}} */
445+
435446
#ifdef ZTS
436447
static void function_copy_ctor(zval *zv)
437448
{
@@ -511,6 +522,8 @@ static void executor_globals_ctor(zend_executor_globals *executor_globals) /* {{
511522
zend_copy_constants(EG(zend_constants), GLOBAL_CONSTANTS_TABLE);
512523
zend_init_rsrc_plist();
513524
zend_init_exception_op();
525+
zend_init_call_trampoline_op();
526+
memset(&executor_globals->trampoline, 0, sizeof(zend_op_array));
514527
executor_globals->lambda_count = 0;
515528
ZVAL_UNDEF(&executor_globals->user_error_handler);
516529
ZVAL_UNDEF(&executor_globals->user_exception_handler);
@@ -722,6 +735,7 @@ int zend_startup(zend_utility_functions *utility_functions, char **extensions) /
722735
#ifndef ZTS
723736
zend_init_rsrc_plist();
724737
zend_init_exception_op();
738+
zend_init_call_trampoline_op();
725739
#endif
726740

727741
zend_ini_startup();

Zend/zend_API.c

+11-23
Original file line numberDiff line numberDiff line change
@@ -3070,16 +3070,7 @@ static int zend_is_callable_check_func(int check_flags, zval *callable, zend_fca
30703070
get_function_via_handler:
30713071
if (fcc->object && fcc->calling_scope == ce_org) {
30723072
if (strict_class && ce_org->__call) {
3073-
fcc->function_handler = emalloc(sizeof(zend_internal_function));
3074-
fcc->function_handler->internal_function.type = ZEND_INTERNAL_FUNCTION;
3075-
fcc->function_handler->internal_function.module = (ce_org->type == ZEND_INTERNAL_CLASS) ? ce_org->info.internal.module : NULL;
3076-
fcc->function_handler->internal_function.handler = zend_std_call_user_call;
3077-
fcc->function_handler->internal_function.arg_info = NULL;
3078-
fcc->function_handler->internal_function.num_args = 0;
3079-
fcc->function_handler->internal_function.scope = ce_org;
3080-
fcc->function_handler->internal_function.fn_flags = ZEND_ACC_CALL_VIA_HANDLER;
3081-
fcc->function_handler->internal_function.function_name = mname;
3082-
zend_string_addref(mname);
3073+
fcc->function_handler = zend_get_call_trampoline_func(ce_org, mname, 0);
30833074
call_via_handler = 1;
30843075
retval = 1;
30853076
} else if (fcc->object->handlers->get_method) {
@@ -3088,15 +3079,15 @@ static int zend_is_callable_check_func(int check_flags, zval *callable, zend_fca
30883079
if (strict_class &&
30893080
(!fcc->function_handler->common.scope ||
30903081
!instanceof_function(ce_org, fcc->function_handler->common.scope))) {
3091-
if ((fcc->function_handler->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) != 0) {
3082+
if (fcc->function_handler->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) {
30923083
if (fcc->function_handler->type != ZEND_OVERLOADED_FUNCTION) {
30933084
zend_string_release(fcc->function_handler->common.function_name);
30943085
}
3095-
efree(fcc->function_handler);
3086+
zend_free_trampoline(fcc->function_handler);
30963087
}
30973088
} else {
30983089
retval = 1;
3099-
call_via_handler = (fcc->function_handler->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) != 0;
3090+
call_via_handler = (fcc->function_handler->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) != 0;
31003091
}
31013092
}
31023093
}
@@ -3108,7 +3099,7 @@ static int zend_is_callable_check_func(int check_flags, zval *callable, zend_fca
31083099
}
31093100
if (fcc->function_handler) {
31103101
retval = 1;
3111-
call_via_handler = (fcc->function_handler->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) != 0;
3102+
call_via_handler = (fcc->function_handler->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) != 0;
31123103
if (call_via_handler && !fcc->object && EG(current_execute_data) && Z_OBJ(EG(current_execute_data)->This) &&
31133104
instanceof_function(Z_OBJCE(EG(current_execute_data)->This), fcc->calling_scope)) {
31143105
fcc->object = Z_OBJ(EG(current_execute_data)->This);
@@ -3250,14 +3241,13 @@ ZEND_API zend_bool zend_is_callable_ex(zval *callable, zend_object *object, uint
32503241
ret = zend_is_callable_check_func(check_flags, callable, fcc, 0, error);
32513242
if (fcc == &fcc_local &&
32523243
fcc->function_handler &&
3253-
((fcc->function_handler->type == ZEND_INTERNAL_FUNCTION &&
3254-
(fcc->function_handler->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER)) ||
3244+
((fcc->function_handler->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) ||
32553245
fcc->function_handler->type == ZEND_OVERLOADED_FUNCTION_TEMPORARY ||
32563246
fcc->function_handler->type == ZEND_OVERLOADED_FUNCTION)) {
32573247
if (fcc->function_handler->type != ZEND_OVERLOADED_FUNCTION) {
32583248
zend_string_release(fcc->function_handler->common.function_name);
32593249
}
3260-
efree(fcc->function_handler);
3250+
zend_free_trampoline(fcc->function_handler);
32613251
}
32623252
return ret;
32633253

@@ -3338,14 +3328,13 @@ ZEND_API zend_bool zend_is_callable_ex(zval *callable, zend_object *object, uint
33383328
ret = zend_is_callable_check_func(check_flags, method, fcc, strict_class, error);
33393329
if (fcc == &fcc_local &&
33403330
fcc->function_handler &&
3341-
((fcc->function_handler->type == ZEND_INTERNAL_FUNCTION &&
3342-
(fcc->function_handler->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER)) ||
3331+
((fcc->function_handler->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) ||
33433332
fcc->function_handler->type == ZEND_OVERLOADED_FUNCTION_TEMPORARY ||
33443333
fcc->function_handler->type == ZEND_OVERLOADED_FUNCTION)) {
33453334
if (fcc->function_handler->type != ZEND_OVERLOADED_FUNCTION) {
33463335
zend_string_release(fcc->function_handler->common.function_name);
33473336
}
3348-
efree(fcc->function_handler);
3337+
zend_free_trampoline(fcc->function_handler);
33493338
}
33503339
return ret;
33513340

@@ -3414,14 +3403,13 @@ ZEND_API zend_bool zend_make_callable(zval *callable, zend_string **callable_nam
34143403
add_next_index_str(callable, zend_string_copy(fcc.function_handler->common.function_name));
34153404
}
34163405
if (fcc.function_handler &&
3417-
((fcc.function_handler->type == ZEND_INTERNAL_FUNCTION &&
3418-
(fcc.function_handler->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER)) ||
3406+
((fcc.function_handler->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) ||
34193407
fcc.function_handler->type == ZEND_OVERLOADED_FUNCTION_TEMPORARY ||
34203408
fcc.function_handler->type == ZEND_OVERLOADED_FUNCTION)) {
34213409
if (fcc.function_handler->type != ZEND_OVERLOADED_FUNCTION) {
34223410
zend_string_release(fcc.function_handler->common.function_name);
34233411
}
3424-
efree(fcc.function_handler);
3412+
zend_free_trampoline(fcc.function_handler);
34253413
}
34263414
return 1;
34273415
}

Zend/zend_builtin_functions.c

+3-6
Original file line numberDiff line numberDiff line change
@@ -1285,16 +1285,14 @@ ZEND_FUNCTION(method_exists)
12851285
&& Z_OBJ_HT_P(klass)->get_method != NULL
12861286
&& (func = Z_OBJ_HT_P(klass)->get_method(&Z_OBJ_P(klass), method_name, NULL)) != NULL
12871287
) {
1288-
if (func->type == ZEND_INTERNAL_FUNCTION
1289-
&& (func->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) != 0
1290-
) {
1288+
if (func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) {
12911289
/* Returns true to the fake Closure's __invoke */
12921290
RETVAL_BOOL(func->common.scope == zend_ce_closure
12931291
&& zend_string_equals_literal(method_name, ZEND_INVOKE_FUNC_NAME));
12941292

12951293
zend_string_release(lcname);
12961294
zend_string_release(func->common.function_name);
1297-
efree(func);
1295+
zend_free_trampoline(func);
12981296
return;
12991297
}
13001298
zend_string_release(lcname);
@@ -2508,8 +2506,7 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int
25082506
if (prev_call &&
25092507
prev_call->func &&
25102508
!ZEND_USER_CODE(prev_call->func->common.type) &&
2511-
!(prev_call->func->common.type == ZEND_INTERNAL_FUNCTION &&
2512-
(prev_call->func->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER))) {
2509+
!(prev_call->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) {
25132510
break;
25142511
}
25152512
if (prev->func && ZEND_USER_CODE(prev->func->common.type)) {

Zend/zend_compile.h

+5-2
Original file line numberDiff line numberDiff line change
@@ -226,8 +226,11 @@ typedef struct _zend_try_catch_element {
226226
#define ZEND_ACC_CLOSURE 0x100000
227227
#define ZEND_ACC_GENERATOR 0x800000
228228

229-
/* function flag for internal user call handlers __call, __callstatic */
230-
#define ZEND_ACC_CALL_VIA_HANDLER 0x200000
229+
/* call through user function trampoline. e.g. __call, __callstatic */
230+
#define ZEND_ACC_CALL_VIA_TRAMPOLINE 0x200000
231+
232+
/* call through internal function handler. e.g. Closure::invoke() */
233+
#define ZEND_ACC_CALL_VIA_HANDLER ZEND_ACC_CALL_VIA_TRAMPOLINE
231234

232235
/* disable inline caching */
233236
#define ZEND_ACC_NEVER_CACHE 0x400000

Zend/zend_execute_API.c

+7-2
Original file line numberDiff line numberDiff line change
@@ -803,7 +803,7 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) /
803803
Z_ADDREF_P(arg);
804804
} else {
805805
if (Z_ISREF_P(arg) &&
806-
(func->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) == 0 ) {
806+
!(func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) {
807807
/* don't separate references for __call */
808808
arg = Z_REFVAL_P(arg);
809809
}
@@ -827,6 +827,7 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) /
827827
}
828828

829829
if (func->type == ZEND_USER_FUNCTION) {
830+
int call_via_handler = (func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) != 0;
830831
EG(scope) = func->common.scope;
831832
call->symbol_table = fci->symbol_table;
832833
if (UNEXPECTED(func->op_array.fn_flags & ZEND_ACC_CLOSURE)) {
@@ -839,8 +840,12 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) /
839840
} else {
840841
zend_generator_create_zval(call, &func->op_array, fci->retval);
841842
}
843+
if (call_via_handler) {
844+
/* We must re-initialize function again */
845+
fci_cache->initialized = 0;
846+
}
842847
} else if (func->type == ZEND_INTERNAL_FUNCTION) {
843-
int call_via_handler = (func->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) != 0;
848+
int call_via_handler = (func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) != 0;
844849
ZVAL_NULL(fci->retval);
845850
if (func->common.scope) {
846851
EG(scope) = func->common.scope;

Zend/zend_globals.h

+4
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include "zend_modules.h"
3737
#include "zend_float.h"
3838
#include "zend_multibyte.h"
39+
#include "zend_multiply.h"
3940
#include "zend_arena.h"
4041

4142
/* Define ZTS if you want a thread-safe Zend */
@@ -237,6 +238,9 @@ struct _zend_executor_globals {
237238
XPFPA_CW_DATATYPE saved_fpu_cw;
238239
#endif
239240

241+
zend_function trampoline;
242+
zend_op call_trampoline_op;
243+
240244
void *reserved[ZEND_MAX_RESERVED_RESOURCES];
241245
};
242246

0 commit comments

Comments
 (0)