@@ -97,16 +97,18 @@ ZEND_API zend_execute_data* zend_generator_freeze_call_stack(zend_execute_data *
97
97
/* }}} */
98
98
99
99
static void zend_generator_cleanup_unfinished_execution (
100
- zend_generator * generator , uint32_t catch_op_num ) /* {{{ */
100
+ zend_generator * generator , zend_execute_data * execute_data , uint32_t catch_op_num ) /* {{{ */
101
101
{
102
- zend_execute_data * execute_data = generator -> execute_data ;
103
-
104
102
if (execute_data -> opline != execute_data -> func -> op_array .opcodes ) {
105
103
/* -1 required because we want the last run opcode, not the next to-be-run one. */
106
104
uint32_t op_num = execute_data -> opline - execute_data -> func -> op_array .opcodes - 1 ;
107
105
108
106
if (UNEXPECTED (generator -> frozen_call_stack )) {
107
+ /* Temporarily restore generator->execute_data if it has been NULLed out already. */
108
+ zend_execute_data * save_ex = generator -> execute_data ;
109
+ generator -> execute_data = execute_data ;
109
110
zend_generator_restore_call_stack (generator );
111
+ generator -> execute_data = save_ex ;
110
112
}
111
113
zend_cleanup_unfinished_execution (execute_data , op_num , catch_op_num );
112
114
}
@@ -117,6 +119,9 @@ ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished
117
119
{
118
120
if (EXPECTED (generator -> execute_data )) {
119
121
zend_execute_data * execute_data = generator -> execute_data ;
122
+ /* Null out execute_data early, to prevent double frees if GC runs while we're
123
+ * already cleaning up execute_data. */
124
+ generator -> execute_data = NULL ;
120
125
121
126
if (EX_CALL_INFO () & ZEND_CALL_HAS_SYMBOL_TABLE ) {
122
127
zend_clean_and_cache_symbol_table (execute_data -> symbol_table );
@@ -135,12 +140,12 @@ ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished
135
140
return ;
136
141
}
137
142
138
- zend_vm_stack_free_extra_args (generator -> execute_data );
143
+ zend_vm_stack_free_extra_args (execute_data );
139
144
140
145
/* Some cleanups are only necessary if the generator was closed
141
146
* before it could finish execution (reach a return statement). */
142
147
if (UNEXPECTED (!finished_execution )) {
143
- zend_generator_cleanup_unfinished_execution (generator , 0 );
148
+ zend_generator_cleanup_unfinished_execution (generator , execute_data , 0 );
144
149
}
145
150
146
151
/* Free closure object */
@@ -154,8 +159,7 @@ ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished
154
159
generator -> gc_buffer = NULL ;
155
160
}
156
161
157
- efree (generator -> execute_data );
158
- generator -> execute_data = NULL ;
162
+ efree (execute_data );
159
163
}
160
164
}
161
165
/* }}} */
@@ -216,7 +220,7 @@ static void zend_generator_dtor_storage(zend_object *object) /* {{{ */
216
220
if (finally_op_num ) {
217
221
zval * fast_call ;
218
222
219
- zend_generator_cleanup_unfinished_execution (generator , finally_op_num );
223
+ zend_generator_cleanup_unfinished_execution (generator , ex , finally_op_num );
220
224
221
225
fast_call = ZEND_CALL_VAR (ex , ex -> func -> op_array .opcodes [finally_op_end ].op1 .var );
222
226
Z_OBJ_P (fast_call ) = EG (exception );
0 commit comments