From 75ef3c7c17939974201637e48f6dc558ea70abf1 Mon Sep 17 00:00:00 2001 From: Max Semenik Date: Tue, 24 Jan 2023 23:18:07 +0300 Subject: [PATCH] Fix GH-10437: Segfault on suspending a dead fiber --- Zend/tests/fibers/gh10437.phpt | 24 ++++++++++++++++++++++++ Zend/zend_fibers.c | 29 ++++++++++++++++++++++++++++- 2 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 Zend/tests/fibers/gh10437.phpt diff --git a/Zend/tests/fibers/gh10437.phpt b/Zend/tests/fibers/gh10437.phpt new file mode 100644 index 0000000000000..2e9af7ef61a95 --- /dev/null +++ b/Zend/tests/fibers/gh10437.phpt @@ -0,0 +1,24 @@ +--TEST-- +GH-10437 (Segfault on suspending a dead fiber) +--FILE-- +suspend(); +}); + +$fiber = new Fiber(function () { + while (1); +}); +$fiber->start(); +--EXPECTF-- +Fatal error: Maximum execution time of 1 second exceeded in %s + +Fatal error: Uncaught FiberError: Cannot suspend a dead fiber in %s +Stack trace: +#0 %s: Fiber::suspend() +#1 [internal function]: {closure}() +#2 {main} + thrown in %s diff --git a/Zend/zend_fibers.c b/Zend/zend_fibers.c index a6168f17fd6e8..5e41e254fc31d 100644 --- a/Zend/zend_fibers.c +++ b/Zend/zend_fibers.c @@ -768,6 +768,26 @@ static HashTable *zend_fiber_object_gc(zend_object *object, zval **table, int *n return lastSymTable; } +static const char* zend_fiber_status_to_string(zend_fiber *fiber) +{ + switch (fiber->context.status) { + case ZEND_FIBER_STATUS_INIT: + return "initializing"; + break; + case ZEND_FIBER_STATUS_RUNNING: + return "running"; + break; + case ZEND_FIBER_STATUS_SUSPENDED: + return "suspended"; + break; + case ZEND_FIBER_STATUS_DEAD: + return "dead"; + break; + } + + return "(unknown status)"; +} + ZEND_METHOD(Fiber, __construct) { zend_fcall_info fci; @@ -846,7 +866,14 @@ ZEND_METHOD(Fiber, suspend) RETURN_THROWS(); } - ZEND_ASSERT(fiber->context.status == ZEND_FIBER_STATUS_RUNNING || fiber->context.status == ZEND_FIBER_STATUS_SUSPENDED); + if (UNEXPECTED(fiber->context.status != ZEND_FIBER_STATUS_RUNNING && fiber->context.status != ZEND_FIBER_STATUS_SUSPENDED)) { + zend_throw_error(zend_ce_fiber_error, + "Cannot suspend %s %s fiber", + ((fiber->context.status == ZEND_FIBER_STATUS_INIT) ? "an" : "a"), + zend_fiber_status_to_string(fiber) + ); + RETURN_THROWS(); + } fiber->execute_data = EG(current_execute_data); fiber->stack_bottom->prev_execute_data = NULL;