Skip to content

Commit b61b60d

Browse files
committed
Merge branch 'PHP-7.2' into PHP-7.3
* PHP-7.2: Fix bug #78752
2 parents 7469953 + 16c4910 commit b61b60d

File tree

3 files changed

+38
-8
lines changed

3 files changed

+38
-8
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ PHP NEWS
66
. Fixed bug #78658 (Memory corruption using Closure::bindTo). (Nikita)
77
. Fixed bug #78656 (Parse errors classified as highest log-level). (Erik
88
Lundin)
9+
. Fixed bug #78752 (Segfault if GC triggered while generator stack frame is
10+
being destroyed). (Nikita)
911

1012
- COM:
1113
. Fixed bug #78694 (Appending to a variant array causes segfault). (cmb)

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
@@ -96,16 +96,18 @@ ZEND_API zend_execute_data* zend_generator_freeze_call_stack(zend_execute_data *
9696
/* }}} */
9797

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

107105
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;
108109
zend_generator_restore_call_stack(generator);
110+
generator->execute_data = save_ex;
109111
}
110112
zend_cleanup_unfinished_execution(execute_data, op_num, catch_op_num);
111113
}
@@ -116,6 +118,9 @@ ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished
116118
{
117119
if (EXPECTED(generator->execute_data)) {
118120
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;
119124

120125
if (EX_CALL_INFO() & ZEND_CALL_HAS_SYMBOL_TABLE) {
121126
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
134139
return;
135140
}
136141

137-
zend_vm_stack_free_extra_args(generator->execute_data);
142+
zend_vm_stack_free_extra_args(execute_data);
138143

139144
/* Some cleanups are only necessary if the generator was closed
140145
* before it could finish execution (reach a return statement). */
141146
if (UNEXPECTED(!finished_execution)) {
142-
zend_generator_cleanup_unfinished_execution(generator, 0);
147+
zend_generator_cleanup_unfinished_execution(generator, execute_data, 0);
143148
}
144149

145150
/* Free closure object */
@@ -153,8 +158,7 @@ ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished
153158
generator->gc_buffer = NULL;
154159
}
155160

156-
efree(generator->execute_data);
157-
generator->execute_data = NULL;
161+
efree(execute_data);
158162
}
159163
}
160164
/* }}} */
@@ -215,7 +219,7 @@ static void zend_generator_dtor_storage(zend_object *object) /* {{{ */
215219
if (finally_op_num) {
216220
zval *fast_call;
217221

218-
zend_generator_cleanup_unfinished_execution(generator, finally_op_num);
222+
zend_generator_cleanup_unfinished_execution(generator, ex, finally_op_num);
219223

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

0 commit comments

Comments
 (0)