Commit 75217c16c89 for php.net
commit 75217c16c89639fe3b01e43d1cdc4a701063347d
Author: Alexandre Daubois <2144837+alexandre-daubois@users.noreply.github.com>
Date: Tue Sep 16 15:05:45 2025 +0200
Fix GH-19801: address leak when calling var_dump() with recursion in __debugInfo() (#19837)
diff --git a/NEWS b/NEWS
index 0891acce266..b3cdd047e50 100644
--- a/NEWS
+++ b/NEWS
@@ -29,6 +29,8 @@ PHP NEWS
. Fixed bug GH-12265 (Cloning an object breaks serialization recursion).
(nielsdos)
. Fixed bug GH-19701 (Serialize/deserialize loses some data). (nielsdos)
+ . Fixed bug GH-19801 (leaks in var_dump() and debug_zval_dump()).
+ (alexandre-daubois)
- Zip:
. Fixed bug GH-19688 (Remove pattern overflow in zip addGlob()). (nielsdos)
diff --git a/ext/standard/tests/general_functions/debug_zval_dump_gh19801_memory_leak.phpt b/ext/standard/tests/general_functions/debug_zval_dump_gh19801_memory_leak.phpt
new file mode 100644
index 00000000000..8d65cd6d185
--- /dev/null
+++ b/ext/standard/tests/general_functions/debug_zval_dump_gh19801_memory_leak.phpt
@@ -0,0 +1,32 @@
+--TEST--
+GH-19801 (debug_zval_dump() leak with __debugInfo() that modifies circular references)
+--FILE--
+<?php
+
+$a = [
+ new class {
+ function __debugInfo() {
+ global $b;
+ $b->a = null;
+ gc_collect_cycles();
+ return [];
+ }
+ },
+];
+
+$b = new stdClass;
+$b->a = &$a;
+
+debug_zval_dump($b);
+?>
+--EXPECTF--
+object(stdClass)#2 (1) refcount(%d){
+ ["a"]=>
+ reference refcount(%d) {
+ array(1) refcount(%d){
+ [0]=>
+ object(class@anonymous)#1 (0) refcount(%d){
+ }
+ }
+ }
+}
diff --git a/ext/standard/tests/general_functions/var_dump_gh19801_memory_leak.phpt b/ext/standard/tests/general_functions/var_dump_gh19801_memory_leak.phpt
new file mode 100644
index 00000000000..f0522916de4
--- /dev/null
+++ b/ext/standard/tests/general_functions/var_dump_gh19801_memory_leak.phpt
@@ -0,0 +1,30 @@
+--TEST--
+GH-19801 (var_dump() memory leak with __debugInfo() that modifies circular references)
+--FILE--
+<?php
+
+$a = [
+ new class {
+ function __debugInfo() {
+ global $b;
+ $b->a = null;
+ gc_collect_cycles();
+ return [];
+ }
+ },
+];
+
+$b = new stdClass;
+$b->a = &$a;
+
+var_dump($b);
+?>
+--EXPECTF--
+object(stdClass)#2 (1) {
+ ["a"]=>
+ &array(1) {
+ [0]=>
+ object(class@anonymous)#1 (0) {
+ }
+ }
+}
diff --git a/ext/standard/var.c b/ext/standard/var.c
index c6e280d15ab..795fb366c60 100644
--- a/ext/standard/var.c
+++ b/ext/standard/var.c
@@ -139,7 +139,7 @@ PHPAPI void php_var_dump(zval *struc, int level) /* {{{ */
} ZEND_HASH_FOREACH_END();
if (!(GC_FLAGS(myht) & GC_IMMUTABLE)) {
GC_UNPROTECT_RECURSION(myht);
- GC_DELREF(myht);
+ GC_DTOR_NO_REF(myht);
}
if (level > 1) {
php_printf("%*c", level-1, ' ');
@@ -336,7 +336,7 @@ PHPAPI void php_debug_zval_dump(zval *struc, int level) /* {{{ */
} ZEND_HASH_FOREACH_END();
if (!(GC_FLAGS(myht) & GC_IMMUTABLE)) {
GC_UNPROTECT_RECURSION(myht);
- GC_DELREF(myht);
+ GC_DTOR_NO_REF(myht);
}
if (level > 1) {
php_printf("%*c", level - 1, ' ');