summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS2
-rw-r--r--Zend/tests/bug70397.phpt15
-rw-r--r--Zend/zend_closures.c32
3 files changed, 38 insertions, 11 deletions
diff --git a/NEWS b/NEWS
index 959ad68f05..4a449400b7 100644
--- a/NEWS
+++ b/NEWS
@@ -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);
}
}