com php-src: Fix potential segfault when finally in a generator is run during shutdown: Zend/tests/generators/finally/run_on_dtor .phpt
Zend/zend_generators.c
| From: | Nikita Popov | Date: | Wed, 30 Jan 2013 22:52:02 +0000 |
| Subject: | com php-src: Fix potential segfault when finally in a generator is run during shutdown: Zend/tests/generators/finally/run_on_dtor .phpt Zend/zend_generators.c |
||
| Groups: | php.cvs | ||
| Request: | Send a blank email to php-cvs+get-71090@lists.php.net to get a copy of this message | ||
Commit: 8b972efe5f3e9d4e33d6e0193f3d56716a6cc59f
Author: Nikita Popov <nikic@php.net> Wed, 30 Jan 2013 23:52:02 +0100
Parents: f540e086e26280036f868c8bb5df458364f71f02
Branches: PHP-5.5 master
Link: http://git.php.net/?p=php-src.git;a=commitdiff;h=8b972efe5f3e9d4e33d6e0193f3d56716a6cc59f
Log:
Fix potential segfault when finally in a generator is run during shutdown
If a generator is destroyed in a finally block it will resume the generator to run that finally
block before freeing the generator. This was done in the object storage free handler.
Running user code in the free handler isn't safe though because the free handlers may be run
during request shutdown, already after several key components have been shut down.
This is avoided by doing the finally handling in the dtor handler. These handlers are run at the
start of the shutdown sequence.
Changed paths:
A Zend/tests/generators/finally/run_on_dtor.phpt
M Zend/zend_generators.c
Diff:
diff --git a/Zend/tests/generators/finally/run_on_dtor.phpt
b/Zend/tests/generators/finally/run_on_dtor.phpt
new file mode 100644
index 0000000..35f8f4e
--- /dev/null
+++ b/Zend/tests/generators/finally/run_on_dtor.phpt
@@ -0,0 +1,22 @@
+--TEST--
+finally is run on object dtor, not free
+--FILE--
+<?php
+
+function gen() {
+ try {
+ yield;
+ } finally {
+ var_dump($_GET);
+ }
+}
+
+$gen = gen();
+$gen->rewind();
+
+set_error_handler(function() use($gen) {});
+
+?>
+--EXPECT--
+array(0) {
+}
diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c
index c6c18a7..e8787d5 100644
--- a/Zend/zend_generators.c
+++ b/Zend/zend_generators.c
@@ -43,41 +43,6 @@ ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished
zend_execute_data *execute_data = generator->execute_data;
zend_op_array *op_array = execute_data->op_array;
- if (!finished_execution) {
- if (op_array->has_finally_block) {
- /* -1 required because we want the last run opcode, not the
- * next to-be-run one. */
- zend_uint op_num = execute_data->opline - op_array->opcodes - 1;
- zend_uint finally_op_num = 0;
-
- /* Find next finally block */
- int i;
- for (i = 0; i < op_array->last_try_catch; i++) {
- zend_try_catch_element *try_catch = &op_array->try_catch_array[i];
-
- if (op_num < try_catch->try_op) {
- break;
- }
-
- if (op_num < try_catch->finally_op) {
- finally_op_num = try_catch->finally_op;
- }
- }
-
- /* If a finally block was found we jump directly to it and
- * resume the generator. Furthermore we abort this close call
- * because the generator will already be closed somewhere in
- * the resume. */
- if (finally_op_num) {
- execute_data->opline = &op_array->opcodes[finally_op_num];
- execute_data->fast_ret = NULL;
- generator->flags |= ZEND_GENERATOR_FORCED_CLOSE;
- zend_generator_resume(generator TSRMLS_CC);
- return;
- }
- }
- }
-
if (!execute_data->symbol_table) {
zend_free_compiled_variables(execute_data);
} else {
@@ -175,6 +140,45 @@ ZEND_API void zend_generator_close(zend_generator *generator, zend_bool
finished
}
/* }}} */
+static void zend_generator_dtor_storage(zend_generator *generator, zend_object_handle handle
TSRMLS_DC) /* {{{ */
+{
+ zend_execute_data *ex = generator->execute_data;
+ zend_uint op_num, finally_op_num;
+ int i;
+
+ if (!ex || !ex->op_array->has_finally_block) {
+ return;
+ }
+
+ /* -1 required because we want the last run opcode, not the
+ * next to-be-run one. */
+ op_num = ex->opline - ex->op_array->opcodes - 1;
+
+ /* Find next finally block */
+ finally_op_num = 0;
+ for (i = 0; i < ex->op_array->last_try_catch; i++) {
+ zend_try_catch_element *try_catch = &ex->op_array->try_catch_array[i];
+
+ if (op_num < try_catch->try_op) {
+ break;
+ }
+
+ if (op_num < try_catch->finally_op) {
+ finally_op_num = try_catch->finally_op;
+ }
+ }
+
+ /* If a finally block was found we jump directly to it and
+ * resume the generator. */
+ if (finally_op_num) {
+ ex->opline = &ex->op_array->opcodes[finally_op_num];
+ ex->fast_ret = NULL;
+ generator->flags |= ZEND_GENERATOR_FORCED_CLOSE;
+ zend_generator_resume(generator TSRMLS_CC);
+ }
+}
+/* }}} */
+
static void zend_generator_free_storage(zend_generator *generator TSRMLS_DC) /* {{{ */
{
zend_generator_close(generator, 0 TSRMLS_CC);
@@ -355,7 +359,8 @@ static zend_object_value zend_generator_create(zend_class_entry *class_type TSRM
zend_object_std_init(&generator->std, class_type TSRMLS_CC);
- object.handle = zend_objects_store_put(generator, NULL,
+ object.handle = zend_objects_store_put(generator,
+ (zend_objects_store_dtor_t) zend_generator_dtor_storage,
(zend_objects_free_object_storage_t) zend_generator_free_storage,
(zend_objects_store_clone_t) zend_generator_clone_storage
TSRMLS_CC