Skip to content

Commit 44e5d25

Browse files
committed
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.
1 parent 61b432c commit 44e5d25

File tree

3 files changed

+61
-7
lines changed

3 files changed

+61
-7
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
--TEST--
2+
Class constant inheritance with mutable data
3+
--FILE--
4+
<?php
5+
6+
// This would previously leak under opcache.
7+
class A {
8+
const X = 'X' . self::Y;
9+
const Y = 'Y';
10+
}
11+
interface I {
12+
const X2 = 'X2' . self::Y2;
13+
const Y2 = 'Y2';
14+
}
15+
eval('class B extends A implements I {}');
16+
var_dump(new B);
17+
var_dump(B::X, B::X2);
18+
19+
// This should only produce one warning, not two.
20+
class X {
21+
const C = 1 % 1.5;
22+
}
23+
class Y extends X {
24+
}
25+
var_dump(X::C, Y::C);
26+
?>
27+
--EXPECTF--
28+
object(B)#1 (0) {
29+
}
30+
string(2) "XY"
31+
string(4) "X2Y2"
32+
33+
Deprecated: Implicit conversion from float 1.5 to int loses precision in %s on line %d
34+
int(0)
35+
int(0)

Zend/zend_API.c

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1322,12 +1322,19 @@ ZEND_API HashTable *zend_separate_class_constants_table(zend_class_entry *class_
13221322
zend_hash_extend(constants_table, zend_hash_num_elements(&class_type->constants_table), 0);
13231323

13241324
ZEND_HASH_FOREACH_STR_KEY_PTR(&class_type->constants_table, key, c) {
1325-
if (Z_TYPE(c->value) == IS_CONSTANT_AST) {
1326-
new_c = zend_arena_alloc(&CG(arena), sizeof(zend_class_constant));
1327-
memcpy(new_c, c, sizeof(zend_class_constant));
1328-
c = new_c;
1325+
if (c->ce == class_type) {
1326+
if (Z_TYPE(c->value) == IS_CONSTANT_AST) {
1327+
new_c = zend_arena_alloc(&CG(arena), sizeof(zend_class_constant));
1328+
memcpy(new_c, c, sizeof(zend_class_constant));
1329+
c = new_c;
1330+
}
1331+
Z_TRY_ADDREF(c->value);
1332+
} else {
1333+
if (Z_TYPE(c->value) == IS_CONSTANT_AST) {
1334+
c = zend_hash_find_ptr(CE_CONSTANTS_TABLE(c->ce), key);
1335+
ZEND_ASSERT(c);
1336+
}
13291337
}
1330-
Z_TRY_ADDREF(c->value);
13311338
_zend_hash_append_ptr(constants_table, key, c);
13321339
} ZEND_HASH_FOREACH_END();
13331340

@@ -1409,8 +1416,18 @@ ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type) /
14091416
} else {
14101417
constants_table = &class_type->constants_table;
14111418
}
1412-
ZEND_HASH_FOREACH_PTR(constants_table, c) {
1419+
1420+
zend_string *name;
1421+
ZEND_HASH_FOREACH_STR_KEY_VAL(constants_table, name, val) {
1422+
c = Z_PTR_P(val);
14131423
if (Z_TYPE(c->value) == IS_CONSTANT_AST) {
1424+
if (c->ce != class_type) {
1425+
Z_PTR_P(val) = c = zend_hash_find_ptr(CE_CONSTANTS_TABLE(c->ce), name);
1426+
if (Z_TYPE(c->value) != IS_CONSTANT_AST) {
1427+
continue;
1428+
}
1429+
}
1430+
14141431
val = &c->value;
14151432
if (UNEXPECTED(zval_update_constant_ex(val, c->ce) != SUCCESS)) {
14161433
return FAILURE;

Zend/zend_opcode.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,9 @@ ZEND_API void zend_cleanup_mutable_class_data(zend_class_entry *ce)
250250
zend_class_constant *c;
251251

252252
ZEND_HASH_FOREACH_PTR(constants_table, c) {
253-
zval_ptr_dtor_nogc(&c->value);
253+
if (c->ce == ce) {
254+
zval_ptr_dtor_nogc(&c->value);
255+
}
254256
} ZEND_HASH_FOREACH_END();
255257
zend_hash_destroy(constants_table);
256258
mutable_data->constants_table = NULL;

0 commit comments

Comments
 (0)