@@ -96,16 +96,18 @@ ZEND_API zend_execute_data* zend_generator_freeze_call_stack(zend_execute_data *
9696/* }}} */
9797
9898static void zend_generator_cleanup_unfinished_execution (
99- zend_generator * generator , uint32_t catch_op_num ) /* {{{ */
99+ zend_generator * generator , zend_execute_data * execute_data , uint32_t catch_op_num ) /* {{{ */
100100{
101- zend_execute_data * execute_data = generator -> execute_data ;
102-
103101 if (execute_data -> opline != execute_data -> func -> op_array .opcodes ) {
104102 /* -1 required because we want the last run opcode, not the next to-be-run one. */
105103 uint32_t op_num = execute_data -> opline - execute_data -> func -> op_array .opcodes - 1 ;
106104
107105 if (UNEXPECTED (generator -> frozen_call_stack )) {
106+ /* Temporarily restore generator->execute_data if it has been NULLed out already. */
107+ zend_execute_data * save_ex = generator -> execute_data ;
108+ generator -> execute_data = execute_data ;
108109 zend_generator_restore_call_stack (generator );
110+ generator -> execute_data = save_ex ;
109111 }
110112 zend_cleanup_unfinished_execution (execute_data , op_num , catch_op_num );
111113 }
@@ -116,6 +118,9 @@ ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished
116118{
117119 if (EXPECTED (generator -> execute_data )) {
118120 zend_execute_data * execute_data = generator -> execute_data ;
121+ /* Null out execute_data early, to prevent double frees if GC runs while we're
122+ * already cleaning up execute_data. */
123+ generator -> execute_data = NULL ;
119124
120125 if (EX_CALL_INFO () & ZEND_CALL_HAS_SYMBOL_TABLE ) {
121126 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
134139 return ;
135140 }
136141
137- zend_vm_stack_free_extra_args (generator -> execute_data );
142+ zend_vm_stack_free_extra_args (execute_data );
138143
139144 /* Some cleanups are only necessary if the generator was closed
140145 * before it could finish execution (reach a return statement). */
141146 if (UNEXPECTED (!finished_execution )) {
142- zend_generator_cleanup_unfinished_execution (generator , 0 );
147+ zend_generator_cleanup_unfinished_execution (generator , execute_data , 0 );
143148 }
144149
145150 /* Free closure object */
@@ -153,8 +158,7 @@ ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished
153158 generator -> gc_buffer = NULL ;
154159 }
155160
156- efree (generator -> execute_data );
157- generator -> execute_data = NULL ;
161+ efree (execute_data );
158162 }
159163}
160164/* }}} */
@@ -215,7 +219,7 @@ static void zend_generator_dtor_storage(zend_object *object) /* {{{ */
215219 if (finally_op_num ) {
216220 zval * fast_call ;
217221
218- zend_generator_cleanup_unfinished_execution (generator , finally_op_num );
222+ zend_generator_cleanup_unfinished_execution (generator , ex , finally_op_num );
219223
220224 fast_call = ZEND_CALL_VAR (ex , ex -> func -> op_array .opcodes [finally_op_end ].op1 .var );
221225 Z_OBJ_P (fast_call ) = EG (exception );
0 commit comments