From 253817273f74e298250fac73c9f919728317b804 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Fri, 30 Jan 2026 15:56:34 +0100 Subject: [PATCH] Real instance of lazy proxy may have less magic methods --- Zend/tests/lazy_objects/gh20504-001.phpt | 24 +++++++++++++++++ Zend/tests/lazy_objects/gh20504-002.phpt | 23 +++++++++++++++++ Zend/tests/lazy_objects/gh20504-003.phpt | 33 ++++++++++++++++++++++++ Zend/tests/lazy_objects/gh20504-004.phpt | 28 ++++++++++++++++++++ Zend/tests/lazy_objects/gh20504-005.phpt | 30 +++++++++++++++++++++ Zend/zend_object_handlers.c | 16 ++++++------ 6 files changed, 146 insertions(+), 8 deletions(-) create mode 100644 Zend/tests/lazy_objects/gh20504-001.phpt create mode 100644 Zend/tests/lazy_objects/gh20504-002.phpt create mode 100644 Zend/tests/lazy_objects/gh20504-003.phpt create mode 100644 Zend/tests/lazy_objects/gh20504-004.phpt create mode 100644 Zend/tests/lazy_objects/gh20504-005.phpt diff --git a/Zend/tests/lazy_objects/gh20504-001.phpt b/Zend/tests/lazy_objects/gh20504-001.phpt new file mode 100644 index 0000000000000..c092e0f337fbc --- /dev/null +++ b/Zend/tests/lazy_objects/gh20504-001.phpt @@ -0,0 +1,24 @@ +--TEST-- +GH-20504: Assertion failure in zend_get_property_guard() when lazy proxy adds magic method - isset +--CREDITS-- +vi3tL0u1s +--FILE-- +$name['']); + } +} +$rc = new ReflectionClass(Proxy::class); +$obj = $rc->newLazyProxy(function () { + return new RealInstance; +}); +var_dump(isset($obj->name[''])); + +?> +--EXPECT-- +bool(false) diff --git a/Zend/tests/lazy_objects/gh20504-002.phpt b/Zend/tests/lazy_objects/gh20504-002.phpt new file mode 100644 index 0000000000000..c9cb7e743af6d --- /dev/null +++ b/Zend/tests/lazy_objects/gh20504-002.phpt @@ -0,0 +1,23 @@ +--TEST-- +GH-20504: Assertion failure in zend_get_property_guard() when lazy proxy adds magic method - get +--FILE-- +$name; + } +} +$rc = new ReflectionClass(Proxy::class); +$obj = $rc->newLazyProxy(function () { + return new RealInstance; +}); +var_dump($obj->name); + +?> +--EXPECTF-- +Warning: Undefined property: RealInstance::$name in %s on line %d +NULL diff --git a/Zend/tests/lazy_objects/gh20504-003.phpt b/Zend/tests/lazy_objects/gh20504-003.phpt new file mode 100644 index 0000000000000..df66e43a875ae --- /dev/null +++ b/Zend/tests/lazy_objects/gh20504-003.phpt @@ -0,0 +1,33 @@ +--TEST-- +GH-20504: Assertion failure in zend_get_property_guard() when lazy proxy adds magic method - set +--FILE-- +$name = $value; + } +} +$rc = new ReflectionClass(Proxy::class); +$obj = $rc->newLazyProxy(function () { + return new RealInstance; +}); +$obj->name = 0; + +var_dump($obj); + +?> +--EXPECTF-- +lazy proxy object(Proxy)#%d (1) { + ["instance"]=> + object(RealInstance)#%d (2) { + ["_"]=> + NULL + ["name"]=> + int(0) + } +} diff --git a/Zend/tests/lazy_objects/gh20504-004.phpt b/Zend/tests/lazy_objects/gh20504-004.phpt new file mode 100644 index 0000000000000..a80964a9ae98c --- /dev/null +++ b/Zend/tests/lazy_objects/gh20504-004.phpt @@ -0,0 +1,28 @@ +--TEST-- +GH-20504: Assertion failure in zend_get_property_guard() when lazy proxy adds magic method - proxy defines __isset(), both have guards +--FILE-- +$name['']); + } +} +$rc = new ReflectionClass(Proxy::class); +$obj = $rc->newLazyProxy(function () { + return new RealInstance; +}); +var_dump(isset($obj->name[''])); + +?> +--EXPECT-- +Proxy::__isset +Proxy::__get +bool(false) diff --git a/Zend/tests/lazy_objects/gh20504-005.phpt b/Zend/tests/lazy_objects/gh20504-005.phpt new file mode 100644 index 0000000000000..8a2519bde114b --- /dev/null +++ b/Zend/tests/lazy_objects/gh20504-005.phpt @@ -0,0 +1,30 @@ +--TEST-- +GH-20504: Assertion failure in zend_get_property_guard() when lazy proxy adds magic method - unset +--FILE-- +$name); + } +} +$rc = new ReflectionClass(Proxy::class); +$obj = $rc->newLazyProxy(function () { + return new RealInstance; +}); +unset($obj->name); + +var_dump($obj); + +?> +--EXPECTF-- +lazy proxy object(Proxy)#%d (1) { + ["instance"]=> + object(RealInstance)#%d (1) { + ["_"]=> + NULL + } +} diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index ccedb79acc082..40203cf525843 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -956,25 +956,25 @@ ZEND_API zval *zend_std_read_property(zend_object *zobj, zend_string *name, int uninit_error: if (UNEXPECTED(zend_lazy_object_must_init(zobj))) { if (!prop_info || (Z_PROP_FLAG_P(retval) & IS_PROP_LAZY)) { - zobj = zend_lazy_object_init(zobj); - if (!zobj) { + zend_object *instance = zend_lazy_object_init(zobj); + if (!instance) { retval = &EG(uninitialized_zval); goto exit; } - if (UNEXPECTED(guard)) { + if (UNEXPECTED(guard && instance->ce->ce_flags & ZEND_ACC_USE_GUARDS)) { uint32_t guard_type = (type == BP_VAR_IS) && zobj->ce->__isset ? IN_ISSET : IN_GET; - guard = zend_get_property_guard(zobj, name); + guard = zend_get_property_guard(instance, name); if (!((*guard) & guard_type)) { (*guard) |= guard_type; - retval = zend_std_read_property(zobj, name, type, cache_slot, rv); + retval = zend_std_read_property(instance, name, type, cache_slot, rv); (*guard) &= ~guard_type; return retval; } } - return zend_std_read_property(zobj, name, type, cache_slot, rv); + return zend_std_read_property(instance, name, type, cache_slot, rv); } } if (type != BP_VAR_IS) { @@ -1013,7 +1013,7 @@ static zval *forward_write_to_lazy_object(zend_object *zobj, return &EG(error_zval); } - if (UNEXPECTED(guarded)) { + if (UNEXPECTED(guarded && instance->ce->ce_flags & ZEND_ACC_USE_GUARDS)) { uint32_t *guard = zend_get_property_guard(instance, name); if (!((*guard) & IN_SET)) { (*guard) |= IN_SET; @@ -1597,7 +1597,7 @@ ZEND_API void zend_std_unset_property(zend_object *zobj, zend_string *name, void return; } - if (UNEXPECTED(guard)) { + if (UNEXPECTED(guard && zobj->ce->ce_flags & ZEND_ACC_USE_GUARDS)) { guard = zend_get_property_guard(zobj, name); if (!((*guard) & IN_UNSET)) { (*guard) |= IN_UNSET;