Commit d9cbc3117c4 for php.net
commit d9cbc3117c4186766f67723c6ed765c80d55fa96
Author: Niels Dossche <7771979+ndossche@users.noreply.github.com>
Date: Wed Jan 21 01:12:55 2026 +0100
Fix hooked object properties overflow
The computed number of properties using zend_hash_num_elements(zobj->properties)
is incorrect when the object contains virtual properties. We don't have a
trivial way to find the number of properties virtual properties that need to be
added to this number, so just append with zend_hash_add_new() instead.
Fixes GH-20479
Closes GH-20988
diff --git a/NEWS b/NEWS
index 7d90a01f51d..9b8840c39cd 100644
--- a/NEWS
+++ b/NEWS
@@ -15,6 +15,7 @@ PHP NEWS
. Fix OSS-Fuzz #438780145 (Nested finally with repeated return type check may
uaf). (ilutov)
. Fixed bug GH-20905 (Lazy proxy bailing __clone assertion). (ilutov)
+ . Fixed bug GH-20479 (Hooked object properties overflow). (ndossche)
- Date:
. Update timelib to 2022.16. (Derick)
diff --git a/Zend/tests/property_hooks/gh20479.phpt b/Zend/tests/property_hooks/gh20479.phpt
new file mode 100644
index 00000000000..c53c4e4240c
--- /dev/null
+++ b/Zend/tests/property_hooks/gh20479.phpt
@@ -0,0 +1,40 @@
+--TEST--
+GH-20479: Hooked object properties overflow
+--CREDITS--
+Viet Hoang Luu (@vi3tL0u1s)
+--FILE--
+<?php
+
+#[AllowDynamicProperties]
+class Trigger {
+ public $a = 'x';
+ public $b = 'x';
+ public $c = 'x';
+ public $d = 'x';
+ public $e = 'x';
+ public $f = 'x';
+ public string $trigger {
+ get {
+ return 'trigger';
+ }
+ }
+}
+
+$obj = new Trigger();
+// Add 2 dynamic props
+$obj->g = $obj->h = 'x';
+var_export($obj);
+
+?>
+--EXPECT--
+\Trigger::__set_state(array(
+ 'a' => 'x',
+ 'b' => 'x',
+ 'c' => 'x',
+ 'd' => 'x',
+ 'e' => 'x',
+ 'f' => 'x',
+ 'trigger' => 'trigger',
+ 'h' => 'x',
+ 'g' => 'x',
+))
diff --git a/Zend/zend_property_hooks.c b/Zend/zend_property_hooks.c
index 01a8afb1693..57e22f23ad7 100644
--- a/Zend/zend_property_hooks.c
+++ b/Zend/zend_property_hooks.c
@@ -121,7 +121,7 @@ static zend_array *zho_build_properties_ex(zend_object *zobj, bool check_access,
if (Z_TYPE_P(prop_value) == IS_INDIRECT) {
continue;
}
- zval *tmp = _zend_hash_append(properties, prop_name, prop_value);
+ zval *tmp = zend_hash_add_new(properties, prop_name, prop_value);
Z_TRY_ADDREF_P(tmp);
} ZEND_HASH_FOREACH_END();
}