From 44e5d253000fcefa8b6e771683e2e5266326f8c7 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 16 Nov 2021 14:15:01 +0100 Subject: [PATCH] Fix inheritance of class constants if mutable data used Class constants from parents 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. Closes GH-7658. --- ...ass_constant_inheritance_mutable_data.phpt | 35 +++++++++++++++++++ Zend/zend_API.c | 29 +++++++++++---- Zend/zend_opcode.c | 4 ++- 3 files changed, 61 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 00000000000..02bef7f0239 --- /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 69da32d7e18..0fdf1a02ce9 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -1322,12 +1322,19 @@ ZEND_API HashTable *zend_separate_class_constants_table(zend_class_entry *class_ zend_hash_extend(constants_table, zend_hash_num_elements(&class_type->constants_table), 0); 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) { + 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; + } + Z_TRY_ADDREF(c->value); + } else { + if (Z_TYPE(c->value) == IS_CONSTANT_AST) { + c = zend_hash_find_ptr(CE_CONSTANTS_TABLE(c->ce), key); + ZEND_ASSERT(c); + } } - Z_TRY_ADDREF(c->value); _zend_hash_append_ptr(constants_table, key, c); } ZEND_HASH_FOREACH_END(); @@ -1409,8 +1416,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) { + 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; + } + } + val = &c->value; if (UNEXPECTED(zval_update_constant_ex(val, c->ce) != SUCCESS)) { return FAILURE; diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index dd515c9b30f..9f97570fe2d 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -250,7 +250,9 @@ ZEND_API void zend_cleanup_mutable_class_data(zend_class_entry *ce) zend_class_constant *c; ZEND_HASH_FOREACH_PTR(constants_table, c) { - zval_ptr_dtor_nogc(&c->value); + if (c->ce == ce) { + zval_ptr_dtor_nogc(&c->value); + } } ZEND_HASH_FOREACH_END(); zend_hash_destroy(constants_table); mutable_data->constants_table = NULL;