summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikita Popov <nikita.ppv@gmail.com>2019-10-28 10:27:46 +0100
committerNikita Popov <nikita.ppv@gmail.com>2019-10-28 10:28:04 +0100
commitb61b60d15bf17aed3daee79db55399a2643d7a60 (patch)
tree919eacbaf1c0163da6d42baa203c49c6fd031f70
parent74699533e54a2b2e6093834652138b6d3a71e5b8 (diff)
parent16c49108763db251151b350e433dde6d1a076250 (diff)
downloadphp-git-b61b60d15bf17aed3daee79db55399a2643d7a60.tar.gz
Merge branch 'PHP-7.2' into PHP-7.3
* PHP-7.2: Fix bug #78752
-rw-r--r--NEWS2
-rw-r--r--Zend/tests/bug78752.phpt24
-rw-r--r--Zend/zend_generators.c20
3 files changed, 38 insertions, 8 deletions
diff --git a/NEWS b/NEWS
index ad3684c7c4..c4e0515754 100644
--- a/NEWS
+++ b/NEWS
@@ -6,6 +6,8 @@ PHP NEWS
. Fixed bug #78658 (Memory corruption using Closure::bindTo). (Nikita)
. Fixed bug #78656 (Parse errors classified as highest log-level). (Erik
Lundin)
+ . Fixed bug #78752 (Segfault if GC triggered while generator stack frame is
+ being destroyed). (Nikita)
- COM:
. Fixed bug #78694 (Appending to a variant array causes segfault). (cmb)
diff --git a/Zend/tests/bug78752.phpt b/Zend/tests/bug78752.phpt
new file mode 100644
index 0000000000..367a44eab5
--- /dev/null
+++ b/Zend/tests/bug78752.phpt
@@ -0,0 +1,24 @@
+--TEST--
+Bug #78752: Segfault if GC triggered while generator stack frame is being destroyed
+--FILE--
+<?php
+
+function gen(&$gen) {
+ $a = new stdClass;
+ $a->a = $a;
+ $b = new stdClass;
+ $b->b = $b;
+ yield 1;
+}
+
+$gen = gen($gen);
+var_dump($gen->current());
+for ($i = 0; $i < 9999; $i++) {
+ $a = new stdClass;
+ $a->a = $a;
+}
+$gen->next();
+
+?>
+--EXPECT--
+int(1)
diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c
index a18a1e8849..316de59489 100644
--- a/Zend/zend_generators.c
+++ b/Zend/zend_generators.c
@@ -96,16 +96,18 @@ ZEND_API zend_execute_data* zend_generator_freeze_call_stack(zend_execute_data *
/* }}} */
static void zend_generator_cleanup_unfinished_execution(
- zend_generator *generator, uint32_t catch_op_num) /* {{{ */
+ zend_generator *generator, zend_execute_data *execute_data, uint32_t catch_op_num) /* {{{ */
{
- zend_execute_data *execute_data = generator->execute_data;
-
if (execute_data->opline != execute_data->func->op_array.opcodes) {
/* -1 required because we want the last run opcode, not the next to-be-run one. */
uint32_t op_num = execute_data->opline - execute_data->func->op_array.opcodes - 1;
if (UNEXPECTED(generator->frozen_call_stack)) {
+ /* Temporarily restore generator->execute_data if it has been NULLed out already. */
+ zend_execute_data *save_ex = generator->execute_data;
+ generator->execute_data = execute_data;
zend_generator_restore_call_stack(generator);
+ generator->execute_data = save_ex;
}
zend_cleanup_unfinished_execution(execute_data, op_num, catch_op_num);
}
@@ -116,6 +118,9 @@ ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished
{
if (EXPECTED(generator->execute_data)) {
zend_execute_data *execute_data = generator->execute_data;
+ /* Null out execute_data early, to prevent double frees if GC runs while we're
+ * already cleaning up execute_data. */
+ generator->execute_data = NULL;
if (EX_CALL_INFO() & ZEND_CALL_HAS_SYMBOL_TABLE) {
zend_clean_and_cache_symbol_table(execute_data->symbol_table);
@@ -134,12 +139,12 @@ ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished
return;
}
- zend_vm_stack_free_extra_args(generator->execute_data);
+ zend_vm_stack_free_extra_args(execute_data);
/* Some cleanups are only necessary if the generator was closed
* before it could finish execution (reach a return statement). */
if (UNEXPECTED(!finished_execution)) {
- zend_generator_cleanup_unfinished_execution(generator, 0);
+ zend_generator_cleanup_unfinished_execution(generator, execute_data, 0);
}
/* Free closure object */
@@ -153,8 +158,7 @@ ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished
generator->gc_buffer = NULL;
}
- efree(generator->execute_data);
- generator->execute_data = NULL;
+ efree(execute_data);
}
}
/* }}} */
@@ -215,7 +219,7 @@ static void zend_generator_dtor_storage(zend_object *object) /* {{{ */
if (finally_op_num) {
zval *fast_call;
- zend_generator_cleanup_unfinished_execution(generator, finally_op_num);
+ zend_generator_cleanup_unfinished_execution(generator, ex, finally_op_num);
fast_call = ZEND_CALL_VAR(ex, ex->func->op_array.opcodes[finally_op_end].op1.var);
Z_OBJ_P(fast_call) = EG(exception);