Skip to content

Commit 0e31e03

Browse files
committed
Merge branch 'PHP-8.1' into PHP-8.2
2 parents e8c64b6 + d721dcc commit 0e31e03

File tree

6 files changed

+66
-6
lines changed

6 files changed

+66
-6
lines changed

NEWS

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ PHP NEWS
2727

2828
- Fiber:
2929
. Fixed assembly on alpine x86. (nielsdos)
30+
. Fixed bug GH-10496 (segfault when garbage collector is invoked inside of
31+
fiber). (Bob, Arnaud)
3032

3133
- FPM:
3234
. Fixed bug GH-10315 (FPM unknown child alert not valid). (Jakub Zelenka)

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
@@ -4422,7 +4422,24 @@ ZEND_API void zend_cleanup_unfinished_execution(zend_execute_data *execute_data,
44224422
cleanup_live_vars(execute_data, op_num, catch_op_num);
44234423
}
44244424

4425-
ZEND_API HashTable *zend_unfinished_execution_gc(zend_execute_data *execute_data, zend_execute_data *call, zend_get_gc_buffer *gc_buffer)
4425+
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)
4426+
{
4427+
bool suspended_by_yield = false;
4428+
4429+
if (Z_TYPE_INFO(EX(This)) & ZEND_CALL_GENERATOR) {
4430+
ZEND_ASSERT(EX(return_value));
4431+
4432+
/* The generator object is stored in EX(return_value) */
4433+
zend_generator *generator = (zend_generator*) EX(return_value);
4434+
ZEND_ASSERT(execute_data == generator->execute_data);
4435+
4436+
suspended_by_yield = !(generator->flags & ZEND_GENERATOR_CURRENTLY_RUNNING);
4437+
}
4438+
4439+
return zend_unfinished_execution_gc_ex(execute_data, call, gc_buffer, suspended_by_yield);
4440+
}
4441+
4442+
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)
44264443
{
44274444
if (!EX(func) || !ZEND_USER_CODE(EX(func)->common.type)) {
44284445
return NULL;
@@ -4458,8 +4475,15 @@ ZEND_API HashTable *zend_unfinished_execution_gc(zend_execute_data *execute_data
44584475
}
44594476

44604477
if (call) {
4461-
/* -1 required because we want the last run opcode, not the next to-be-run one. */
4462-
uint32_t op_num = execute_data->opline - op_array->opcodes - 1;
4478+
uint32_t op_num = execute_data->opline - op_array->opcodes;
4479+
if (suspended_by_yield) {
4480+
/* When the execution was suspended by yield, EX(opline) points to
4481+
* next opline to execute. Otherwise, it points to the opline that
4482+
* suspended execution. */
4483+
op_num--;
4484+
ZEND_ASSERT(EX(func)->op_array.opcodes[op_num].opcode == ZEND_YIELD
4485+
|| EX(func)->op_array.opcodes[op_num].opcode == ZEND_YIELD_FROM);
4486+
}
44634487
zend_unfinished_calls_gc(execute_data, call, op_num, gc_buffer);
44644488
}
44654489

Zend/zend_execute.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,8 @@ ZEND_API void zend_clean_and_cache_symbol_table(zend_array *symbol_table);
412412
ZEND_API void ZEND_FASTCALL zend_free_compiled_variables(zend_execute_data *execute_data);
413413
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);
414414
ZEND_API void zend_cleanup_unfinished_execution(zend_execute_data *execute_data, uint32_t op_num, uint32_t catch_op_num);
415-
ZEND_API HashTable *zend_unfinished_execution_gc(zend_execute_data *execute_data, zend_execute_data *call, zend_get_gc_buffer *gc_buffer);
415+
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);
416+
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);
416417

417418
zval * ZEND_FASTCALL zend_handle_named_arg(
418419
zend_execute_data **call_ptr, zend_string *arg_name,

Zend/zend_fibers.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -670,7 +670,7 @@ static HashTable *zend_fiber_object_gc(zend_object *object, zval **table, int *n
670670
HashTable *lastSymTable = NULL;
671671
zend_execute_data *ex = fiber->execute_data;
672672
for (; ex; ex = ex->prev_execute_data) {
673-
HashTable *symTable = zend_unfinished_execution_gc(ex, ex->call, buf);
673+
HashTable *symTable = zend_unfinished_execution_gc_ex(ex, ex->call, buf, false);
674674
if (symTable) {
675675
if (lastSymTable) {
676676
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)