diff options
-rw-r--r-- | NEWS | 2 | ||||
-rw-r--r-- | Zend/tests/bug70397.phpt | 15 | ||||
-rw-r--r-- | Zend/zend_closures.c | 32 |
3 files changed, 38 insertions, 11 deletions
@@ -15,6 +15,8 @@ PHP NEWS . Fixed bug #70370 (Bundled libtool.m4 doesn't handle FreeBSD 10 when building extensions). (Adam) . Fixed bug causing exception traces with anon classes to be truncated. (Bob) + . Fixed bug #70397 (Segmentation fault when using Closure::call and yield). + (Bob) - Curl: . Fixed bug #70330 (Segmentation Fault with multiple "curl_copy_handle"). diff --git a/Zend/tests/bug70397.phpt b/Zend/tests/bug70397.phpt new file mode 100644 index 0000000000..fdfb1d4363 --- /dev/null +++ b/Zend/tests/bug70397.phpt @@ -0,0 +1,15 @@ +--TEST-- +Bug #70397 (Segmentation fault when using Closure::call and yield) +--FILE-- +<?php + +$f = function () { + $this->value = true; + yield $this->value; +}; + +var_dump($f->call(new class {})->current()); + +?> +--EXPECT-- +bool(true) diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index 637e2dd5fc..50aa7d4188 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -87,7 +87,7 @@ ZEND_METHOD(Closure, call) } zclosure = getThis(); - closure = (zend_closure *)Z_OBJ_P(zclosure); + closure = (zend_closure *) Z_OBJ_P(zclosure); if (closure->func.common.fn_flags & ZEND_ACC_STATIC) { zend_error(E_WARNING, "Cannot bind an instance to a static closure"); @@ -122,22 +122,32 @@ ZEND_METHOD(Closure, call) fci.object = fci_cache.object = newobj; fci_cache.initialized = 1; - my_function = *fci_cache.function_handler; - /* use scope of passed object */ - my_function.common.scope = Z_OBJCE_P(newthis); - 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 != Z_OBJCE_P(newthis)) { - my_function.op_array.run_time_cache = emalloc(my_function.op_array.cache_size); - memset(my_function.op_array.run_time_cache, 0, my_function.op_array.cache_size); + if (fci_cache.function_handler->common.fn_flags & ZEND_ACC_GENERATOR) { + zval new_closure; + zend_create_closure(&new_closure, fci_cache.function_handler, Z_OBJCE_P(newthis), closure->called_scope, newthis); + closure = (zend_closure *) Z_OBJ(new_closure); + fci_cache.function_handler = &closure->func; + } else { + memcpy(&my_function, fci_cache.function_handler, fci_cache.function_handler->type == ZEND_USER_FUNCTION ? sizeof(zend_op_array) : sizeof(zend_internal_function)); + /* use scope of passed object */ + my_function.common.scope = Z_OBJCE_P(newthis); + 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 != Z_OBJCE_P(newthis)) { + my_function.op_array.run_time_cache = emalloc(my_function.op_array.cache_size); + memset(my_function.op_array.run_time_cache, 0, my_function.op_array.cache_size); + } } if (zend_call_function(&fci, &fci_cache) == SUCCESS && Z_TYPE(closure_result) != IS_UNDEF) { ZVAL_COPY_VALUE(return_value, &closure_result); } - if (ZEND_USER_CODE(my_function.type) && closure->func.common.scope != Z_OBJCE_P(newthis)) { + if (fci_cache.function_handler->common.fn_flags & ZEND_ACC_GENERATOR) { + /* copied upon generator creation */ + --GC_REFCOUNT(&closure->std); + } else if (ZEND_USER_CODE(my_function.type) && closure->func.common.scope != Z_OBJCE_P(newthis)) { efree(my_function.op_array.run_time_cache); } } |