mirror of
https://github.com/php/php-src.git
synced 2026-03-24 00:02:20 +01:00
Avoid using a stack allocated zend_function in Closure::call, to avoid prevent crashes on bailout
Having a stack allocated zend_function may cause crashes if the stack is polluted between bailout and the actual unwinding in zend_observer_fcall_end_all. Signed-off-by: Bob Weinand <bobwei9@hotmail.com>
This commit is contained in:
@@ -121,7 +121,6 @@ ZEND_METHOD(Closure, call)
|
||||
zend_closure *closure;
|
||||
zend_fcall_info fci;
|
||||
zend_fcall_info_cache fci_cache;
|
||||
zend_function my_function;
|
||||
zend_object *newobj;
|
||||
zend_class_entry *newclass;
|
||||
|
||||
@@ -142,55 +141,71 @@ ZEND_METHOD(Closure, call)
|
||||
return;
|
||||
}
|
||||
|
||||
if (closure->func.common.fn_flags & ZEND_ACC_GENERATOR) {
|
||||
zval new_closure;
|
||||
zend_create_closure(&new_closure, &closure->func, newclass, closure->called_scope, newthis);
|
||||
closure = (zend_closure *) Z_OBJ(new_closure);
|
||||
fci_cache.function_handler = &closure->func;
|
||||
} else {
|
||||
memcpy(&my_function, &closure->func, closure->func.type == ZEND_USER_FUNCTION ? sizeof(zend_op_array) : sizeof(zend_internal_function));
|
||||
my_function.common.fn_flags &= ~ZEND_ACC_CLOSURE;
|
||||
/* use scope of passed object */
|
||||
my_function.common.scope = newclass;
|
||||
if (closure->func.type == ZEND_INTERNAL_FUNCTION) {
|
||||
my_function.internal_function.handler = closure->orig_internal_handler;
|
||||
}
|
||||
fci_cache.function_handler = &my_function;
|
||||
|
||||
/* Runtime cache relies on bound scope to be immutable, hence we need a separate rt cache in case scope changed */
|
||||
if (ZEND_USER_CODE(my_function.type)
|
||||
&& (closure->func.common.scope != newclass
|
||||
|| (closure->func.common.fn_flags & ZEND_ACC_HEAP_RT_CACHE))) {
|
||||
void *ptr;
|
||||
|
||||
my_function.op_array.fn_flags |= ZEND_ACC_HEAP_RT_CACHE;
|
||||
ptr = emalloc(my_function.op_array.cache_size);
|
||||
ZEND_MAP_PTR_INIT(my_function.op_array.run_time_cache, ptr);
|
||||
memset(ptr, 0, my_function.op_array.cache_size);
|
||||
}
|
||||
}
|
||||
|
||||
fci_cache.called_scope = newclass;
|
||||
fci_cache.object = fci.object = newobj;
|
||||
|
||||
fci.size = sizeof(fci);
|
||||
ZVAL_OBJ(&fci.function_name, &closure->std);
|
||||
ZVAL_UNDEF(&closure_result);
|
||||
fci.retval = &closure_result;
|
||||
|
||||
if (zend_call_function(&fci, &fci_cache) == SUCCESS && Z_TYPE(closure_result) != IS_UNDEF) {
|
||||
if (closure->func.common.fn_flags & ZEND_ACC_GENERATOR) {
|
||||
zval new_closure;
|
||||
zend_create_closure(&new_closure, &closure->func, newclass, closure->called_scope, newthis);
|
||||
closure = (zend_closure *) Z_OBJ(new_closure);
|
||||
fci_cache.function_handler = &closure->func;
|
||||
|
||||
zend_call_function(&fci, &fci_cache);
|
||||
|
||||
/* copied upon generator creation */
|
||||
GC_DELREF(&closure->std);
|
||||
} else {
|
||||
zend_function *my_function;
|
||||
if (ZEND_USER_CODE(closure->func.type)) {
|
||||
my_function = emalloc(sizeof(zend_op_array));
|
||||
memcpy(my_function, &closure->func, sizeof(zend_op_array));
|
||||
} else {
|
||||
my_function = emalloc(sizeof(zend_internal_function));
|
||||
memcpy(my_function, &closure->func, sizeof(zend_internal_function));
|
||||
}
|
||||
my_function->common.fn_flags &= ~ZEND_ACC_CLOSURE;
|
||||
/* use scope of passed object */
|
||||
my_function->common.scope = newclass;
|
||||
if (closure->func.type == ZEND_INTERNAL_FUNCTION) {
|
||||
my_function->internal_function.handler = closure->orig_internal_handler;
|
||||
}
|
||||
fci_cache.function_handler = my_function;
|
||||
|
||||
/* Runtime cache relies on bound scope to be immutable, hence we need a separate rt cache in case scope changed */
|
||||
if (ZEND_USER_CODE(my_function->type)
|
||||
&& (closure->func.common.scope != newclass
|
||||
|| (closure->func.common.fn_flags & ZEND_ACC_HEAP_RT_CACHE))) {
|
||||
void *ptr;
|
||||
|
||||
my_function->op_array.fn_flags |= ZEND_ACC_HEAP_RT_CACHE;
|
||||
ptr = emalloc(my_function->op_array.cache_size);
|
||||
ZEND_MAP_PTR_INIT(my_function->op_array.run_time_cache, ptr);
|
||||
memset(ptr, 0, my_function->op_array.cache_size);
|
||||
}
|
||||
|
||||
zend_call_function(&fci, &fci_cache);
|
||||
|
||||
if (ZEND_USER_CODE(my_function->type)) {
|
||||
if (fci_cache.function_handler->common.fn_flags & ZEND_ACC_HEAP_RT_CACHE) {
|
||||
efree(ZEND_MAP_PTR(my_function->op_array.run_time_cache));
|
||||
}
|
||||
efree_size(my_function, sizeof(zend_op_array));
|
||||
} else {
|
||||
efree_size(my_function, sizeof(zend_internal_function));
|
||||
}
|
||||
}
|
||||
|
||||
if (Z_TYPE(closure_result) != IS_UNDEF) {
|
||||
if (Z_ISREF(closure_result)) {
|
||||
zend_unwrap_reference(&closure_result);
|
||||
}
|
||||
ZVAL_COPY_VALUE(return_value, &closure_result);
|
||||
}
|
||||
|
||||
if (fci_cache.function_handler->common.fn_flags & ZEND_ACC_GENERATOR) {
|
||||
/* copied upon generator creation */
|
||||
GC_DELREF(&closure->std);
|
||||
} else if (ZEND_USER_CODE(my_function.type)
|
||||
&& (fci_cache.function_handler->common.fn_flags & ZEND_ACC_HEAP_RT_CACHE)) {
|
||||
efree(ZEND_MAP_PTR(my_function.op_array.run_time_cache));
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
|
||||
Reference in New Issue
Block a user