Skip to content

Commit e8d16fd

Browse files
committed
Merge branch 'PHP-8.2'
2 parents 3b75f07 + 0e31e03 commit e8d16fd

File tree

5 files changed

+64
-6
lines changed

5 files changed

+64
-6
lines changed

Zend/tests/fibers/gh10496.phpt

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
--TEST--
2+
Bug GH-10496 (Segfault when garbage collector is invoked inside of fiber)
3+
--FILE--
4+
<?php
5+
6+
function x(&$ref) {
7+
$ref = new class() {
8+
function __destruct() {
9+
print "Dtor x()\n";
10+
}
11+
};
12+
}
13+
function suspend($x) {
14+
Fiber::suspend();
15+
}
16+
$f = new Fiber(function() use (&$f) {
17+
try {
18+
x($var);
19+
\ord(suspend(1));
20+
} finally {
21+
print "Cleaned\n";
22+
}
23+
});
24+
$f->start();
25+
unset($f);
26+
gc_collect_cycles();
27+
print "Collected\n";
28+
29+
?>
30+
--EXPECT--
31+
Cleaned
32+
Dtor x()
33+
Collected

Zend/zend_execute.c

+27-3
Original file line numberDiff line numberDiff line change
@@ -4494,7 +4494,24 @@ ZEND_API void zend_cleanup_unfinished_execution(zend_execute_data *execute_data,
44944494
cleanup_live_vars(execute_data, op_num, catch_op_num);
44954495
}
44964496

4497-
ZEND_API HashTable *zend_unfinished_execution_gc(zend_execute_data *execute_data, zend_execute_data *call, zend_get_gc_buffer *gc_buffer)
4497+
ZEND_API ZEND_ATTRIBUTE_DEPRECATED HashTable *zend_unfinished_execution_gc(zend_execute_data *execute_data, zend_execute_data *call, zend_get_gc_buffer *gc_buffer)
4498+
{
4499+
bool suspended_by_yield = false;
4500+
4501+
if (Z_TYPE_INFO(EX(This)) & ZEND_CALL_GENERATOR) {
4502+
ZEND_ASSERT(EX(return_value));
4503+
4504+
/* The generator object is stored in EX(return_value) */
4505+
zend_generator *generator = (zend_generator*) EX(return_value);
4506+
ZEND_ASSERT(execute_data == generator->execute_data);
4507+
4508+
suspended_by_yield = !(generator->flags & ZEND_GENERATOR_CURRENTLY_RUNNING);
4509+
}
4510+
4511+
return zend_unfinished_execution_gc_ex(execute_data, call, gc_buffer, suspended_by_yield);
4512+
}
4513+
4514+
ZEND_API HashTable *zend_unfinished_execution_gc_ex(zend_execute_data *execute_data, zend_execute_data *call, zend_get_gc_buffer *gc_buffer, bool suspended_by_yield)
44984515
{
44994516
if (!EX(func) || !ZEND_USER_CODE(EX(func)->common.type)) {
45004517
return NULL;
@@ -4530,8 +4547,15 @@ ZEND_API HashTable *zend_unfinished_execution_gc(zend_execute_data *execute_data
45304547
}
45314548

45324549
if (call) {
4533-
/* -1 required because we want the last run opcode, not the next to-be-run one. */
4534-
uint32_t op_num = execute_data->opline - op_array->opcodes - 1;
4550+
uint32_t op_num = execute_data->opline - op_array->opcodes;
4551+
if (suspended_by_yield) {
4552+
/* When the execution was suspended by yield, EX(opline) points to
4553+
* next opline to execute. Otherwise, it points to the opline that
4554+
* suspended execution. */
4555+
op_num--;
4556+
ZEND_ASSERT(EX(func)->op_array.opcodes[op_num].opcode == ZEND_YIELD
4557+
|| EX(func)->op_array.opcodes[op_num].opcode == ZEND_YIELD_FROM);
4558+
}
45354559
zend_unfinished_calls_gc(execute_data, call, op_num, gc_buffer);
45364560
}
45374561

Zend/zend_execute.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -423,7 +423,8 @@ ZEND_API void zend_clean_and_cache_symbol_table(zend_array *symbol_table);
423423
ZEND_API void ZEND_FASTCALL zend_free_compiled_variables(zend_execute_data *execute_data);
424424
ZEND_API void zend_unfinished_calls_gc(zend_execute_data *execute_data, zend_execute_data *call, uint32_t op_num, zend_get_gc_buffer *buf);
425425
ZEND_API void zend_cleanup_unfinished_execution(zend_execute_data *execute_data, uint32_t op_num, uint32_t catch_op_num);
426-
ZEND_API HashTable *zend_unfinished_execution_gc(zend_execute_data *execute_data, zend_execute_data *call, zend_get_gc_buffer *gc_buffer);
426+
ZEND_API ZEND_ATTRIBUTE_DEPRECATED HashTable *zend_unfinished_execution_gc(zend_execute_data *execute_data, zend_execute_data *call, zend_get_gc_buffer *gc_buffer);
427+
ZEND_API HashTable *zend_unfinished_execution_gc_ex(zend_execute_data *execute_data, zend_execute_data *call, zend_get_gc_buffer *gc_buffer, bool suspended_by_yield);
427428

428429
zval * ZEND_FASTCALL zend_handle_named_arg(
429430
zend_execute_data **call_ptr, zend_string *arg_name,

Zend/zend_fibers.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -751,7 +751,7 @@ static HashTable *zend_fiber_object_gc(zend_object *object, zval **table, int *n
751751
HashTable *lastSymTable = NULL;
752752
zend_execute_data *ex = fiber->execute_data;
753753
for (; ex; ex = ex->prev_execute_data) {
754-
HashTable *symTable = zend_unfinished_execution_gc(ex, ex->call, buf);
754+
HashTable *symTable = zend_unfinished_execution_gc_ex(ex, ex->call, buf, false);
755755
if (symTable) {
756756
if (lastSymTable) {
757757
zval *val;

Zend/zend_generators.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,7 @@ static HashTable *zend_generator_get_gc(zend_object *object, zval **table, int *
372372
call = zend_generator_revert_call_stack(generator->frozen_call_stack);
373373
}
374374

375-
zend_unfinished_execution_gc(execute_data, call, gc_buffer);
375+
zend_unfinished_execution_gc_ex(execute_data, call, gc_buffer, true);
376376

377377
if (UNEXPECTED(generator->frozen_call_stack)) {
378378
zend_generator_revert_call_stack(call);

0 commit comments

Comments
 (0)