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