mirror of
https://github.com/php/php-src.git
synced 2025-08-15 21:48:51 +02:00
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.
This commit is contained in:
parent
61b432ca24
commit
44e5d25300
3 changed files with 61 additions and 7 deletions
35
Zend/tests/class_constant_inheritance_mutable_data.phpt
Normal file
35
Zend/tests/class_constant_inheritance_mutable_data.phpt
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
--TEST--
|
||||||
|
Class constant inheritance with mutable data
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
// This would previously leak under opcache.
|
||||||
|
class A {
|
||||||
|
const X = 'X' . self::Y;
|
||||||
|
const Y = 'Y';
|
||||||
|
}
|
||||||
|
interface I {
|
||||||
|
const X2 = 'X2' . self::Y2;
|
||||||
|
const Y2 = 'Y2';
|
||||||
|
}
|
||||||
|
eval('class B extends A implements I {}');
|
||||||
|
var_dump(new B);
|
||||||
|
var_dump(B::X, B::X2);
|
||||||
|
|
||||||
|
// This should only produce one warning, not two.
|
||||||
|
class X {
|
||||||
|
const C = 1 % 1.5;
|
||||||
|
}
|
||||||
|
class Y extends X {
|
||||||
|
}
|
||||||
|
var_dump(X::C, Y::C);
|
||||||
|
?>
|
||||||
|
--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)
|
|
@ -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_extend(constants_table, zend_hash_num_elements(&class_type->constants_table), 0);
|
||||||
|
|
||||||
ZEND_HASH_FOREACH_STR_KEY_PTR(&class_type->constants_table, key, c) {
|
ZEND_HASH_FOREACH_STR_KEY_PTR(&class_type->constants_table, key, c) {
|
||||||
if (Z_TYPE(c->value) == IS_CONSTANT_AST) {
|
if (c->ce == class_type) {
|
||||||
new_c = zend_arena_alloc(&CG(arena), sizeof(zend_class_constant));
|
if (Z_TYPE(c->value) == IS_CONSTANT_AST) {
|
||||||
memcpy(new_c, c, sizeof(zend_class_constant));
|
new_c = zend_arena_alloc(&CG(arena), sizeof(zend_class_constant));
|
||||||
c = new_c;
|
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_append_ptr(constants_table, key, c);
|
||||||
} ZEND_HASH_FOREACH_END();
|
} ZEND_HASH_FOREACH_END();
|
||||||
|
|
||||||
|
@ -1409,8 +1416,18 @@ ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type) /
|
||||||
} else {
|
} else {
|
||||||
constants_table = &class_type->constants_table;
|
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 (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;
|
val = &c->value;
|
||||||
if (UNEXPECTED(zval_update_constant_ex(val, c->ce) != SUCCESS)) {
|
if (UNEXPECTED(zval_update_constant_ex(val, c->ce) != SUCCESS)) {
|
||||||
return FAILURE;
|
return FAILURE;
|
||||||
|
|
|
@ -250,7 +250,9 @@ ZEND_API void zend_cleanup_mutable_class_data(zend_class_entry *ce)
|
||||||
zend_class_constant *c;
|
zend_class_constant *c;
|
||||||
|
|
||||||
ZEND_HASH_FOREACH_PTR(constants_table, 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_FOREACH_END();
|
||||||
zend_hash_destroy(constants_table);
|
zend_hash_destroy(constants_table);
|
||||||
mutable_data->constants_table = NULL;
|
mutable_data->constants_table = NULL;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue