Skip to content

Commit 1ddda13

Browse files
committed
Merge branch 'PHP-7.3' into PHP-7.4
* PHP-7.3: Fix bug #78752
2 parents 43dc7da + b61b60d commit 1ddda13

File tree

2 files changed

+36
-8
lines changed

2 files changed

+36
-8
lines changed

Zend/tests/bug78752.phpt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
--TEST--
2+
Bug #78752: Segfault if GC triggered while generator stack frame is being destroyed
3+
--FILE--
4+
<?php
5+
6+
function gen(&$gen) {
7+
$a = new stdClass;
8+
$a->a = $a;
9+
$b = new stdClass;
10+
$b->b = $b;
11+
yield 1;
12+
}
13+
14+
$gen = gen($gen);
15+
var_dump($gen->current());
16+
for ($i = 0; $i < 9999; $i++) {
17+
$a = new stdClass;
18+
$a->a = $a;
19+
}
20+
$gen->next();
21+
22+
?>
23+
--EXPECT--
24+
int(1)

Zend/zend_generators.c

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -93,16 +93,18 @@ ZEND_API zend_execute_data* zend_generator_freeze_call_stack(zend_execute_data *
9393
/* }}} */
9494

9595
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) /* {{{ */
9797
{
98-
zend_execute_data *execute_data = generator->execute_data;
99-
10098
if (execute_data->opline != execute_data->func->op_array.opcodes) {
10199
/* -1 required because we want the last run opcode, not the next to-be-run one. */
102100
uint32_t op_num = execute_data->opline - execute_data->func->op_array.opcodes - 1;
103101

104102
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;
105106
zend_generator_restore_call_stack(generator);
107+
generator->execute_data = save_ex;
106108
}
107109
zend_cleanup_unfinished_execution(execute_data, op_num, catch_op_num);
108110
}
@@ -113,6 +115,9 @@ ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished
113115
{
114116
if (EXPECTED(generator->execute_data)) {
115117
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;
116121

117122
if (EX_CALL_INFO() & ZEND_CALL_HAS_SYMBOL_TABLE) {
118123
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
131136
return;
132137
}
133138

134-
zend_vm_stack_free_extra_args(generator->execute_data);
139+
zend_vm_stack_free_extra_args(execute_data);
135140

136141
/* Some cleanups are only necessary if the generator was closed
137142
* before it could finish execution (reach a return statement). */
138143
if (UNEXPECTED(!finished_execution)) {
139-
zend_generator_cleanup_unfinished_execution(generator, 0);
144+
zend_generator_cleanup_unfinished_execution(generator, execute_data, 0);
140145
}
141146

142147
/* Free closure object */
@@ -150,8 +155,7 @@ ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished
150155
generator->gc_buffer = NULL;
151156
}
152157

153-
efree(generator->execute_data);
154-
generator->execute_data = NULL;
158+
efree(execute_data);
155159
}
156160
}
157161
/* }}} */
@@ -212,7 +216,7 @@ static void zend_generator_dtor_storage(zend_object *object) /* {{{ */
212216
if (finally_op_num) {
213217
zval *fast_call;
214218

215-
zend_generator_cleanup_unfinished_execution(generator, finally_op_num);
219+
zend_generator_cleanup_unfinished_execution(generator, ex, finally_op_num);
216220

217221
fast_call = ZEND_CALL_VAR(ex, ex->func->op_array.opcodes[finally_op_end].op1.var);
218222
Z_OBJ_P(fast_call) = EG(exception);

0 commit comments

Comments
 (0)