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