[php-src] master: Merge branch 'PHP-8.6'
| From: | David Carlier | Date: | Fri, 02 Oct 2026 10:18:43 +0000 |
| Subject: | [php-src] master: Merge branch 'PHP-8.6' | ||
| Groups: | php.cvs | ||
| Request: | Send a blank email to php-cvs+get-141878@lists.php.net to get a copy of this message | ||
Author: David Carlier (devnexen)
Date: 2026-10-02T11:18:29+01:00
Commit: https://github.com/php/php-src/commit/03bf9cdcac9ac5f6b7164e4a8670a838e1ded6dd
Raw diff: https://github.com/php/php-src/commit/03bf9cdcac9ac5f6b7164e4a8670a838e1ded6dd.diff
Merge branch 'PHP-8.6'
* PHP-8.6:
Fix GH-24050: gc_collect_white() frees data reachable from a resurrected object
Changed paths:
A Zend/tests/gh24050.phpt
A Zend/tests/gh24050_2.phpt
M Zend/zend_gc.c
Diff:
diff --git a/Zend/tests/gh24050.phpt b/Zend/tests/gh24050.phpt
new file mode 100644
index 000000000000..d5c77fa43dd8
--- /dev/null
+++ b/Zend/tests/gh24050.phpt
@@ -0,0 +1,49 @@
+--TEST--
+GH-24050 (GC frees an object still held by a resurrected closure when (object) and use share an
array)
+--CREDITS--
+OllieCrook
+--FILE--
+<?php
+
+class Resurrector
+{
+ public $closure;
+ public $self;
+
+ public function __destruct()
+ {
+ $GLOBALS['resurrected'] = $this->closure;
+ }
+}
+
+class Holder
+{
+ public $castObject;
+ public $resurrector;
+ public $self;
+}
+
+$array = ['victim' => new stdClass(), 'key' => 'value'];
+
+$holder = new Holder();
+$resurrector = new Resurrector();
+$holder->castObject = (object) $array;
+$resurrector->closure = function () use ($array) {
+ return $array;
+};
+$holder->resurrector = $resurrector;
+$holder->self = $holder;
+$resurrector->self = $resurrector;
+
+unset($array);
+gc_collect_cycles();
+
+unset($holder, $resurrector);
+gc_collect_cycles();
+gc_collect_cycles();
+
+var_dump($resurrected()['victim']);
+?>
+--EXPECT--
+object(stdClass)#1 (0) {
+}
diff --git a/Zend/tests/gh24050_2.phpt b/Zend/tests/gh24050_2.phpt
new file mode 100644
index 000000000000..a4f49d96a4e8
--- /dev/null
+++ b/Zend/tests/gh24050_2.phpt
@@ -0,0 +1,49 @@
+--TEST--
+GH-24050 (GC frees an object still held by a resurrected closure when (array) and use share a
properties table)
+--FILE--
+<?php
+
+class Resurrector
+{
+ public $closure;
+ public $self;
+
+ public function __destruct()
+ {
+ $GLOBALS['resurrected'] = $this->closure;
+ }
+}
+
+class Holder
+{
+ public $obj;
+ public $resurrector;
+ public $self;
+}
+
+$obj = new stdClass();
+$obj->victim = new stdClass();
+
+$holder = new Holder();
+$resurrector = new Resurrector();
+$holder->obj = $obj;
+$array = (array) $obj;
+$resurrector->closure = function () use ($array) {
+ return $array;
+};
+$holder->resurrector = $resurrector;
+$holder->self = $holder;
+$resurrector->self = $resurrector;
+
+unset($array, $obj);
+gc_collect_cycles();
+
+unset($holder, $resurrector);
+gc_collect_cycles();
+gc_collect_cycles();
+
+var_dump($resurrected()['victim']);
+?>
+--EXPECT--
+object(stdClass)#2 (0) {
+}
diff --git a/Zend/zend_gc.c b/Zend/zend_gc.c
index 6c7e2a728e93..6372ed0550c8 100644
--- a/Zend/zend_gc.c
+++ b/Zend/zend_gc.c
@@ -1574,6 +1574,9 @@ static int gc_collect_white(zend_refcounted *ref, uint32_t *flags, gc_stack
*sta
GC_ADDREF(ht);
if (GC_REF_CHECK_COLOR(ht, GC_WHITE)) {
GC_REF_SET_BLACK(ht);
+ if (!GC_INFO(ht)) {
+ gc_add_garbage((zend_refcounted *)ht);
+ }
for (; n != 0; n--) {
if (Z_COLLECTABLE_P(zv)) {
ref = Z_COUNTED_P(zv);