[php-src] PHP-8.6: Merge branch 'PHP-8.4' into PHP-8.5
| From: | David Carlier | Date: | Thu, 22 Oct 2026 19:28:56 +0000 |
| Subject: | [php-src] PHP-8.6: Merge branch 'PHP-8.4' into PHP-8.5 | ||
| Groups: | php.cvs | ||
| Request: | Send a blank email to php-cvs+get-142610@lists.php.net to get a copy of this message | ||
Author: David Carlier (devnexen)
Date: 2026-10-22T20:26:07+01:00
Commit: https://github.com/php/php-src/commit/267ec8e12b42cf410d908ad7174a57e8735fcbd5
Raw diff: https://github.com/php/php-src/commit/267ec8e12b42cf410d908ad7174a57e8735fcbd5.diff
Merge branch 'PHP-8.4' into PHP-8.5
* PHP-8.4:
Fix GH-24373: serialize() context reused by destructors during teardown
Changed paths:
A ext/standard/tests/serialize/gh24373.phpt
A ext/standard/tests/serialize/gh24373_unserialize.phpt
M ext/standard/var.c
M ext/standard/var_unserializer.re
Diff:
diff --git a/ext/standard/tests/serialize/gh24373.phpt b/ext/standard/tests/serialize/gh24373.phpt
new file mode 100644
index 000000000000..14da98ade0d0
--- /dev/null
+++ b/ext/standard/tests/serialize/gh24373.phpt
@@ -0,0 +1,40 @@
+--TEST--
+GH-24373 (serialize() called from a destructor during var_hash destruction reuses the outer
var_hash)
+--CREDITS--
+janhelke
+--FILE--
+<?php
+class D {
+ public function __serialize(): array { return []; }
+ public function __destruct() {
+ $o = new stdClass();
+ $GLOBALS['inner'] = serialize([$o, $o]);
+ }
+}
+class Outer {
+ public function __serialize(): array { return ["d" => new D()]; }
+}
+serialize([new Outer(), new stdClass()]);
+echo $GLOBALS['inner'], PHP_EOL;
+
+class D2 {
+ public function __serialize(): array { return []; }
+ public function __destruct() {
+ $a = [];
+ for ($i = 0; $i < 2000; $i++) { $o = new stdClass(); $a[] = $o; $a[] = $o; }
+ serialize($a);
+ }
+}
+class Outer2 {
+ public function __serialize(): array { return ["d" => new D2()]; }
+}
+$shared = [];
+for ($i = 0; $i < 200; $i++) { $shared[] = new stdClass(); }
+$list = [new Outer2()];
+foreach ($shared as $o) { $list[] = $o; $list[] = $o; }
+serialize($list);
+echo "survived", PHP_EOL;
+?>
+--EXPECT--
+a:2:{i:0;O:8:"stdClass":0:{}i:1;r:2;}
+survived
diff --git a/ext/standard/tests/serialize/gh24373_unserialize.phpt
b/ext/standard/tests/serialize/gh24373_unserialize.phpt
new file mode 100644
index 000000000000..91bd6ee6bca0
--- /dev/null
+++ b/ext/standard/tests/serialize/gh24373_unserialize.phpt
@@ -0,0 +1,14 @@
+--TEST--
+GH-24373 (unserialize() called from a destructor during var_hash destruction reuses the outer
var_hash)
+--FILE--
+<?php
+class D {
+ public function __destruct() {
+ $GLOBALS['r'] =
unserialize('a:2:{i:0;O:8:"stdClass":0:{}i:1;r:2;}');
+ }
+}
+unserialize('a:2:{i:0;O:1:"D":0:{}i:0;i:1;}');
+var_dump($GLOBALS['r'][0] === $GLOBALS['r'][1]);
+?>
+--EXPECT--
+bool(true)
diff --git a/ext/standard/var.c b/ext/standard/var.c
index a9ef4950af22..8906d914d5dc 100644
--- a/ext/standard/var.c
+++ b/ext/standard/var.c
@@ -1425,12 +1425,13 @@ PHPAPI void
php_var_serialize_set_incomplete_classes_only(php_serialize_data_t d
PHPAPI void php_var_serialize_destroy(php_serialize_data_t d) {
/* fprintf(stderr, "SERIALIZE_DESTROY == lock: %u, level: %u\n", BG(serialize_lock),
BG(serialize).level); */
- if (BG(serialize_lock) || BG(serialize).level == 1) {
+ if (BG(serialize_lock)) {
zend_hash_destroy(&d->ht);
efree(d);
- }
- if (!BG(serialize_lock) && !--BG(serialize).level) {
+ } else if (!--BG(serialize).level) {
BG(serialize).data = NULL;
+ zend_hash_destroy(&d->ht);
+ efree(d);
}
}
diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re
index 43968dd40c9e..e5cc1acd758c 100644
--- a/ext/standard/var_unserializer.re
+++ b/ext/standard/var_unserializer.re
@@ -89,12 +89,13 @@ PHPAPI php_unserialize_data_t php_var_unserialize_init(void) {
PHPAPI void php_var_unserialize_destroy(php_unserialize_data_t d) {
/* fprintf(stderr, "UNSERIALIZE_DESTROY == lock: %u, level: %u\n", BG(serialize_lock),
BG(unserialize).level); */
- if (BG(serialize_lock) || BG(unserialize).level == 1) {
+ if (BG(serialize_lock)) {
var_destroy(&d);
efree(d);
- }
- if (!BG(serialize_lock) && !--BG(unserialize).level) {
+ } else if (!--BG(unserialize).level) {
BG(unserialize).data = NULL;
+ var_destroy(&d);
+ efree(d);
}
}