[php-src] master: Fix GH-24373: serialize() context reused by destructors during teardown

From: Date: Thu, 22 Oct 2026 19:28:56 +0000
Subject: [php-src] master: Fix GH-24373: serialize() context reused by destructors during teardown
Groups: php.cvs 
Request: Send a blank email to php-cvs+get-142609@lists.php.net to get a copy of this message
Author: David Carlier (devnexen) Date: 2026-10-22T20:24:43+01:00 Commit: https://github.com/php/php-src/commit/08f4b64e0ae69e51ca95b54717dc74405ba5f0f9 Raw diff: https://github.com/php/php-src/commit/08f4b64e0ae69e51ca95b54717dc74405ba5f0f9.diff Fix GH-24373: serialize() context reused by destructors during teardown The context was destroyed while still registered. Unregister it first. Close GH-24452 Changed paths: A ext/standard/tests/serialize/gh24373.phpt A ext/standard/tests/serialize/gh24373_unserialize.phpt M NEWS M ext/standard/var.c M ext/standard/var_unserializer.re Diff: diff --git a/NEWS b/NEWS index a35e4ce8132c..22760f8d7d1b 100644 --- a/NEWS +++ b/NEWS @@ -133,6 +133,8 @@ PHP NEWS (Jorg Sowa) . Don't share the unserialize() context with enum autoloaders. (Nicolas Grekas) + . Fixed bug GH-24373 (serialize() called from a destructor during var_hash + destruction reuses the outer var_hash). (David Carlier) - Streams: . Fixed bug GH-24508 (HTTP wrapper leaks the dechunk filter when header 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); } }

« previous php.cvs (#142609) next »