From 30c8ceb62a1c1443d1028657c762275010d828d1 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Tue, 15 Jul 2025 20:36:38 +0200 Subject: [PATCH 1/3] Fix properties_info_table for abstract properties Fixes GH-19053 --- Zend/tests/gh19053.phpt | 26 ++++++++++++++++++++++++++ Zend/zend_inheritance.c | 20 ++++++++++++++++++-- 2 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 Zend/tests/gh19053.phpt diff --git a/Zend/tests/gh19053.phpt b/Zend/tests/gh19053.phpt new file mode 100644 index 0000000000000..7c82f1bc5f412 --- /dev/null +++ b/Zend/tests/gh19053.phpt @@ -0,0 +1,26 @@ +--TEST-- +GH-19053: Incorrect properties_info_table for abstract properties +--FILE-- + 2; } +} + +$c = new C; +var_dump($c); + +?> +--EXPECTF-- +object(C)#%d (0) { + ["foo"]=> + uninitialized(mixed) +} diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 6f399fbfec5ab..ddd4575c58eac 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -1687,10 +1687,26 @@ void zend_build_properties_info_table(zend_class_entry *ce) } } - ZEND_HASH_MAP_FOREACH_PTR(&ce->properties_info, prop) { + ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->properties_info, zend_string *key, prop) { if (prop->ce == ce && (prop->flags & ZEND_ACC_STATIC) == 0 && !(prop->flags & ZEND_ACC_VIRTUAL)) { - uint32_t prop_table_offset = OBJ_PROP_TO_NUM(!(prop->prototype->flags & ZEND_ACC_VIRTUAL) ? prop->prototype->offset : prop->offset); + const zend_property_info *root_prop = prop->prototype; + if (UNEXPECTED(root_prop->flags & ZEND_ACC_VIRTUAL)) { + /* Prototype is virtual, we need to manually hunt down the first backed property. */ + root_prop = prop; + while (true) { + zend_class_entry *parent_ce = root_prop->ce->parent; + if (!parent_ce) { break; } + zend_property_info *parent_prop = zend_hash_find_ptr(&parent_ce->properties_info, key); + if (!parent_prop + || parent_prop->prototype != prop->prototype + || (parent_prop->flags & ZEND_ACC_VIRTUAL)) { + break; + } + root_prop = parent_prop; + } + } + uint32_t prop_table_offset = OBJ_PROP_TO_NUM(root_prop->offset); table[prop_table_offset] = prop; } } ZEND_HASH_FOREACH_END(); From 1bbbd19d1326ac18dcf4365bb8756e279b4d216b Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Wed, 16 Jul 2025 17:13:25 +0200 Subject: [PATCH 2/3] Use ZEND_HASH_MAP macro --- Zend/zend_inheritance.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index ddd4575c58eac..65748fe8acb29 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -1687,7 +1687,7 @@ void zend_build_properties_info_table(zend_class_entry *ce) } } - ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->properties_info, zend_string *key, prop) { + ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&ce->properties_info, zend_string *key, prop) { if (prop->ce == ce && (prop->flags & ZEND_ACC_STATIC) == 0 && !(prop->flags & ZEND_ACC_VIRTUAL)) { const zend_property_info *root_prop = prop->prototype; From ca3813a7cfc7054e1f433e1bedc2b3acd52951ad Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Mon, 21 Jul 2025 15:05:02 +0200 Subject: [PATCH 3/3] Embed initial stop cond into while cond (@bwoebi) Co-authored-by: Bob Weinand --- Zend/zend_inheritance.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 65748fe8acb29..ced75ad82d9db 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -1694,9 +1694,8 @@ void zend_build_properties_info_table(zend_class_entry *ce) if (UNEXPECTED(root_prop->flags & ZEND_ACC_VIRTUAL)) { /* Prototype is virtual, we need to manually hunt down the first backed property. */ root_prop = prop; - while (true) { - zend_class_entry *parent_ce = root_prop->ce->parent; - if (!parent_ce) { break; } + zend_class_entry *parent_ce; + while ((parent_ce = root_prop->ce->parent)) { zend_property_info *parent_prop = zend_hash_find_ptr(&parent_ce->properties_info, key); if (!parent_prop || parent_prop->prototype != prop->prototype