diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 872c3df5a10..32e474b0464 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -1313,6 +1313,12 @@ ZEND_API zend_function *zend_get_call_trampoline_func(const zend_class_entry *ce ZEND_MAP_PTR_INIT(func->run_time_cache, (void**)dummy); func->scope = fbc->common.scope; /* reserve space for arguments, local and temporary variables */ + /* EG(trampoline) is reused from other places, like FFI (e.g. zend_ffi_cdata_get_closure()) where + * it is used as an internal function. It may set fields that don't belong to common, thus + * modifying zend_op_array specific data, most significantly last_var. We need to reset this + * value so that it doesn't contain garbage when the engine allocates space for the next stack + * frame. This didn't cause any issues until now due to "lucky" structure layout. */ + func->last_var = 0; func->T = (fbc->type == ZEND_USER_FUNCTION)? MAX(fbc->op_array.last_var + fbc->op_array.T, 2) : 2; func->filename = (fbc->type == ZEND_USER_FUNCTION)? fbc->op_array.filename : ZSTR_EMPTY_ALLOC(); func->line_start = (fbc->type == ZEND_USER_FUNCTION)? fbc->op_array.line_start : 0; diff --git a/ext/ffi/tests/trampoline_reset.phpt b/ext/ffi/tests/trampoline_reset.phpt new file mode 100644 index 00000000000..3a87ec99d2a --- /dev/null +++ b/ext/ffi/tests/trampoline_reset.phpt @@ -0,0 +1,43 @@ +--TEST-- +Memory corruption when mixing __callStatic() and FFI +--EXTENSIONS-- +ffi +--SKIPIF-- + +--INI-- +ffi.enable=1 +--FILE-- +fprintf($ffi->stdout, "FFI\n"); +$ffi->fflush($ffi->stdout); +Test::baz(); +?> +--EXPECT-- +foo called +bar called +FFI +baz called