From b351fec70df6eb99081d3a8187985fe34ed02a75 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 16 Nov 2021 14:15:01 +0100 Subject: [PATCH 1/2] Fix inheritance of class constants if mutable data used Class constants from parent should always be directly reused, rather than re-evaluated as a separate copy. Previously this used to happen automatically, as we'd just inherit the class constant entry from the parent class. With mutable data there may now be a separate copy of the constant, so we need to use that copy when updating constants. Otherwise we may evaluate the same constant multiple times. --- ...ass_constant_inheritance_mutable_data.phpt | 35 ++++++++++++++++ Zend/zend_API.c | 41 +++++++++++++++---- 2 files changed, 69 insertions(+), 7 deletions(-) create mode 100644 Zend/tests/class_constant_inheritance_mutable_data.phpt diff --git a/Zend/tests/class_constant_inheritance_mutable_data.phpt b/Zend/tests/class_constant_inheritance_mutable_data.phpt new file mode 100644 index 0000000000000..02bef7f02392e --- /dev/null +++ b/Zend/tests/class_constant_inheritance_mutable_data.phpt @@ -0,0 +1,35 @@ +--TEST-- +Class constant inheritance with mutable data +--FILE-- + +--EXPECTF-- +object(B)#1 (0) { +} +string(2) "XY" +string(4) "X2Y2" + +Deprecated: Implicit conversion from float 1.5 to int loses precision in %s on line %d +int(0) +int(0) diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 69da32d7e18f5..fdae52ed048d0 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -1310,6 +1310,22 @@ static zend_class_mutable_data *zend_allocate_mutable_data(zend_class_entry *cla } /* }}} */ +static zend_class_constant *inherit_class_constant(zend_class_entry *ce, zend_string *name) { + if (ce->parent) { + zend_class_constant *c = zend_hash_find_ptr(CE_CONSTANTS_TABLE(ce->parent), name); + if (c) { + return c; + } + } + for (uint32_t i = 0; i < ce->num_interfaces; i++) { + zend_class_constant *c = zend_hash_find_ptr(CE_CONSTANTS_TABLE(ce->interfaces[i]), name); + if (c) { + return c; + } + } + ZEND_UNREACHABLE(); +} + ZEND_API HashTable *zend_separate_class_constants_table(zend_class_entry *class_type) /* {{{ */ { zend_class_mutable_data *mutable_data; @@ -1323,9 +1339,13 @@ ZEND_API HashTable *zend_separate_class_constants_table(zend_class_entry *class_ ZEND_HASH_FOREACH_STR_KEY_PTR(&class_type->constants_table, key, c) { if (Z_TYPE(c->value) == IS_CONSTANT_AST) { - new_c = zend_arena_alloc(&CG(arena), sizeof(zend_class_constant)); - memcpy(new_c, c, sizeof(zend_class_constant)); - c = new_c; + if (c->ce == class_type) { + new_c = zend_arena_alloc(&CG(arena), sizeof(zend_class_constant)); + memcpy(new_c, c, sizeof(zend_class_constant)); + c = new_c; + } else { + c = inherit_class_constant(class_type, key); + } } Z_TRY_ADDREF(c->value); _zend_hash_append_ptr(constants_table, key, c); @@ -1409,11 +1429,18 @@ ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type) / } else { constants_table = &class_type->constants_table; } - ZEND_HASH_FOREACH_PTR(constants_table, c) { + + zend_string *name; + ZEND_HASH_FOREACH_STR_KEY_VAL(constants_table, name, val) { + c = Z_PTR_P(val); if (Z_TYPE(c->value) == IS_CONSTANT_AST) { - val = &c->value; - if (UNEXPECTED(zval_update_constant_ex(val, c->ce) != SUCCESS)) { - return FAILURE; + if (c->ce == class_type) { + val = &c->value; + if (UNEXPECTED(zval_update_constant_ex(val, c->ce) != SUCCESS)) { + return FAILURE; + } + } else { + Z_PTR_P(val) = inherit_class_constant(class_type, name); } } } ZEND_HASH_FOREACH_END(); From d9f89b92516cf6472af2cd41fb0f747339ef378b Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Wed, 17 Nov 2021 10:15:44 +0100 Subject: [PATCH 2/2] Simplify patch --- Zend/zend_API.c | 34 +++++++++++----------------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/Zend/zend_API.c b/Zend/zend_API.c index fdae52ed048d0..20f867ffbdf1a 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -1310,22 +1310,6 @@ static zend_class_mutable_data *zend_allocate_mutable_data(zend_class_entry *cla } /* }}} */ -static zend_class_constant *inherit_class_constant(zend_class_entry *ce, zend_string *name) { - if (ce->parent) { - zend_class_constant *c = zend_hash_find_ptr(CE_CONSTANTS_TABLE(ce->parent), name); - if (c) { - return c; - } - } - for (uint32_t i = 0; i < ce->num_interfaces; i++) { - zend_class_constant *c = zend_hash_find_ptr(CE_CONSTANTS_TABLE(ce->interfaces[i]), name); - if (c) { - return c; - } - } - ZEND_UNREACHABLE(); -} - ZEND_API HashTable *zend_separate_class_constants_table(zend_class_entry *class_type) /* {{{ */ { zend_class_mutable_data *mutable_data; @@ -1344,7 +1328,8 @@ ZEND_API HashTable *zend_separate_class_constants_table(zend_class_entry *class_ memcpy(new_c, c, sizeof(zend_class_constant)); c = new_c; } else { - c = inherit_class_constant(class_type, key); + c = zend_hash_find_ptr(CE_CONSTANTS_TABLE(c->ce), key); + ZEND_ASSERT(c); } } Z_TRY_ADDREF(c->value); @@ -1434,13 +1419,16 @@ ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type) / ZEND_HASH_FOREACH_STR_KEY_VAL(constants_table, name, val) { c = Z_PTR_P(val); if (Z_TYPE(c->value) == IS_CONSTANT_AST) { - if (c->ce == class_type) { - val = &c->value; - if (UNEXPECTED(zval_update_constant_ex(val, c->ce) != SUCCESS)) { - return FAILURE; + if (c->ce != class_type) { + Z_PTR_P(val) = c = zend_hash_find_ptr(CE_CONSTANTS_TABLE(c->ce), name); + if (Z_TYPE(c->value) != IS_CONSTANT_AST) { + continue; } - } else { - Z_PTR_P(val) = inherit_class_constant(class_type, name); + } + + val = &c->value; + if (UNEXPECTED(zval_update_constant_ex(val, c->ce) != SUCCESS)) { + return FAILURE; } } } ZEND_HASH_FOREACH_END();