Skip to content

Commit 9f98fdd

Browse files
committed
Merge branch 'PHP-7.4'
* PHP-7.4: Fix bug #78752
2 parents addc78f + 1ddda13 commit 9f98fdd

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
@@ -94,16 +94,18 @@ ZEND_API zend_execute_data* zend_generator_freeze_call_stack(zend_execute_data *
9494
/* }}} */
9595

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

105103
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;
106107
zend_generator_restore_call_stack(generator);
108+
generator->execute_data = save_ex;
107109
}
108110
zend_cleanup_unfinished_execution(execute_data, op_num, catch_op_num);
109111
}
@@ -114,6 +116,9 @@ ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished
114116
{
115117
if (EXPECTED(generator->execute_data)) {
116118
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;
117122

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

135-
zend_vm_stack_free_extra_args(generator->execute_data);
140+
zend_vm_stack_free_extra_args(execute_data);
136141

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

143148
/* Free closure object */
@@ -151,8 +156,7 @@ ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished
151156
generator->gc_buffer = NULL;
152157
}
153158

154-
efree(generator->execute_data);
155-
generator->execute_data = NULL;
159+
efree(execute_data);
156160
}
157161
}
158162
/* }}} */
@@ -213,7 +217,7 @@ static void zend_generator_dtor_storage(zend_object *object) /* {{{ */
213217
if (finally_op_num) {
214218
zval *fast_call;
215219

216-
zend_generator_cleanup_unfinished_execution(generator, finally_op_num);
220+
zend_generator_cleanup_unfinished_execution(generator, ex, finally_op_num);
217221

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

0 commit comments

Comments
 (0)