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