[php-src] master: Merge branch 'PHP-8.6'

From: Date: Thu, 08 Oct 2026 14:54:39 +0000
Subject: [php-src] master: Merge branch 'PHP-8.6'
Groups: php.cvs 
Request: Send a blank email to php-cvs+get-142085@lists.php.net to get a copy of this message
Author: Weilin Du (LamentXU123) Date: 2026-10-08T22:46:15+08:00 Commit: https://github.com/php/php-src/commit/222c13f3e0b660de0873bdefaee7ca644f92531e Raw diff: https://github.com/php/php-src/commit/222c13f3e0b660de0873bdefaee7ca644f92531e.diff Merge branch 'PHP-8.6' * PHP-8.6: Fix leak when the added previous exception is already in the chain (#24177) Changed paths: A Zend/tests/exception_previous_already_in_chain_destructor.phpt A Zend/tests/fibers/gc-destructor-throw-previous-already-in-chain.phpt A Zend/tests/fibers/unfinished-fiber-with-throw-previous-already-in-chain.phpt A Zend/tests/generators/finally/yield_throw_previous_already_in_chain.phpt A Zend/tests/try/try_finally_previous_already_in_chain.phpt M Zend/zend_exceptions.c Diff: diff --git a/Zend/tests/exception_previous_already_in_chain_destructor.phpt b/Zend/tests/exception_previous_already_in_chain_destructor.phpt new file mode 100644 index 000000000000..d302609aa76c --- /dev/null +++ b/Zend/tests/exception_previous_already_in_chain_destructor.phpt @@ -0,0 +1,29 @@ +--TEST-- +Exception thrown in a destructor during unwinding that already has the pending exception in its previous chain +--FILE-- +<?php +class ThrowsInDestructor { + public function __construct(private Throwable $pending) {} + + public function __destruct() { + throw new LogicException("thrown", 0, $this->pending); + } +} + +function f() { + $pending = new RuntimeException("pending"); + $object = new ThrowsInDestructor($pending); + throw $pending; +} + +try { + f(); +} catch (Throwable $t) { + for (; $t !== null; $t = $t->getPrevious()) { + echo $t->getMessage(), "\n"; + } +} +?> +--EXPECT-- +thrown +pending diff --git a/Zend/tests/fibers/gc-destructor-throw-previous-already-in-chain.phpt b/Zend/tests/fibers/gc-destructor-throw-previous-already-in-chain.phpt new file mode 100644 index 000000000000..0e50ab9fb29a --- /dev/null +++ b/Zend/tests/fibers/gc-destructor-throw-previous-already-in-chain.phpt @@ -0,0 +1,41 @@ +--TEST-- +Exception thrown by a GC destructor that already has the exception of a previous, suspended destructor in its previous chain +--FILE-- +<?php +class Cycle { + public static ?Throwable $pending = null; + public $self; + + public function __construct() { + $this->self = $this; + } + + public function __destruct() { + if (self::$pending === null) { + try { + Fiber::suspend(); + } finally { + self::$pending = new RuntimeException("pending"); + throw self::$pending; + } + } + throw new LogicException("thrown", 0, self::$pending); + } +} + +$fiber = new Fiber(function () { + new Cycle; + new Cycle; + try { + gc_collect_cycles(); + } catch (Throwable $t) { + for (; $t !== null; $t = $t->getPrevious()) { + echo $t->getMessage(), "\n"; + } + } +}); +$fiber->start(); +?> +--EXPECT-- +thrown +pending diff --git a/Zend/tests/fibers/unfinished-fiber-with-throw-previous-already-in-chain.phpt b/Zend/tests/fibers/unfinished-fiber-with-throw-previous-already-in-chain.phpt new file mode 100644 index 000000000000..cf27c8eeff02 --- /dev/null +++ b/Zend/tests/fibers/unfinished-fiber-with-throw-previous-already-in-chain.phpt @@ -0,0 +1,28 @@ +--TEST-- +Exception thrown in finally of a fiber destroyed during unwinding that already has the pending exception in its previous chain +--FILE-- +<?php +function f() { + $pending = new RuntimeException("pending"); + $fiber = new Fiber(function () use ($pending) { + try { + Fiber::suspend(); + } finally { + throw new LogicException("thrown", 0, $pending); + } + }); + $fiber->start(); + throw $pending; +} + +try { + f(); +} catch (Throwable $t) { + for (; $t !== null; $t = $t->getPrevious()) { + echo $t->getMessage(), "\n"; + } +} +?> +--EXPECT-- +thrown +pending diff --git a/Zend/tests/generators/finally/yield_throw_previous_already_in_chain.phpt b/Zend/tests/generators/finally/yield_throw_previous_already_in_chain.phpt new file mode 100644 index 000000000000..9205eedbc5ea --- /dev/null +++ b/Zend/tests/generators/finally/yield_throw_previous_already_in_chain.phpt @@ -0,0 +1,30 @@ +--TEST-- +Exception thrown in finally of a generator destroyed during unwinding that already has the pending exception in its previous chain +--FILE-- +<?php +function gen(Throwable $pending) { + try { + yield; + } finally { + throw new LogicException("thrown", 0, $pending); + } +} + +function f() { + $pending = new RuntimeException("pending"); + $generator = gen($pending); + $generator->current(); + throw $pending; +} + +try { + f(); +} catch (Throwable $t) { + for (; $t !== null; $t = $t->getPrevious()) { + echo $t->getMessage(), "\n"; + } +} +?> +--EXPECT-- +thrown +pending diff --git a/Zend/tests/try/try_finally_previous_already_in_chain.phpt b/Zend/tests/try/try_finally_previous_already_in_chain.phpt new file mode 100644 index 000000000000..fd0bdc20cb79 --- /dev/null +++ b/Zend/tests/try/try_finally_previous_already_in_chain.phpt @@ -0,0 +1,38 @@ +--TEST-- +Exception thrown in finally that already has the pending exception in its previous chain +--FILE-- +<?php +function direct() { + $e = new RuntimeException("pending"); + try { + throw $e; + } finally { + throw new LogicException("thrown", 0, $e); + } +} + +function nested() { + $e = new RuntimeException("pending"); + try { + throw $e; + } finally { + throw new LogicException("thrown", 0, new Exception("middle", 0, $e)); + } +} + +foreach (['direct', 'nested'] as $function) { + try { + $function(); + } catch (Throwable $t) { + for (; $t !== null; $t = $t->getPrevious()) { + echo $t->getMessage(), "\n"; + } + } +} +?> +--EXPECT-- +thrown +pending +thrown +middle +pending diff --git a/Zend/zend_exceptions.c b/Zend/zend_exceptions.c index 0af55b7fbe9b..34dec6b8558b 100644 --- a/Zend/zend_exceptions.c +++ b/Zend/zend_exceptions.c @@ -140,6 +140,8 @@ void zend_exception_set_previous(zend_object *exception, zend_object *add_previo } ex = previous; } while (Z_OBJ_P(ex) != add_previous); + + OBJ_RELEASE(add_previous); } /* }}} */

« previous php.cvs (#142085) next »