Skip to content

Commit 99f72fa

Browse files
committed
Merge branch 'PHP-8.4'
* PHP-8.4: Fix infinite recursion on deprecated attribute evaluation
2 parents ab6e464 + 272f7f7 commit 99f72fa

File tree

8 files changed

+73
-9
lines changed

8 files changed

+73
-9
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
--TEST--
2+
GH-17711: Infinite recursion through deprecated class constants self-referencing through deprecation message
3+
--FILE--
4+
<?php
5+
6+
class C {
7+
#[\Deprecated(self::C)]
8+
const C = TEST;
9+
}
10+
11+
const TEST = 'Message';
12+
var_dump(C::C);
13+
14+
class D {
15+
#[\Deprecated(Alias::C)]
16+
const C = 'test';
17+
}
18+
19+
class_alias('D', 'Alias');
20+
var_dump(D::C);
21+
22+
?>
23+
--EXPECTF--
24+
Deprecated: Constant C::C is deprecated, Message in %s on line %d
25+
string(7) "Message"
26+
27+
Deprecated: Constant D::C is deprecated, test in %s on line %d
28+
string(4) "test"

Zend/zend_API.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -1439,7 +1439,7 @@ ZEND_API HashTable *zend_separate_class_constants_table(zend_class_entry *class_
14391439

14401440
ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&class_type->constants_table, key, c) {
14411441
if (c->ce == class_type) {
1442-
if (Z_TYPE(c->value) == IS_CONSTANT_AST) {
1442+
if (Z_TYPE(c->value) == IS_CONSTANT_AST || (ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED)) {
14431443
new_c = zend_arena_alloc(&CG(arena), sizeof(zend_class_constant));
14441444
memcpy(new_c, c, sizeof(zend_class_constant));
14451445
c = new_c;

Zend/zend_compile.c

+4
Original file line numberDiff line numberDiff line change
@@ -8862,6 +8862,10 @@ static void zend_compile_class_const_decl(zend_ast *ast, uint32_t flags, zend_as
88628862

88638863
if (deprecated) {
88648864
ZEND_CLASS_CONST_FLAGS(c) |= ZEND_ACC_DEPRECATED;
8865+
/* For deprecated constants, we need to flag the zval for recursion
8866+
* detection. Make sure the zval is separated out of shm. */
8867+
ce->ce_flags |= ZEND_ACC_HAS_AST_CONSTANTS;
8868+
ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
88658869
}
88668870
}
88678871
}

Zend/zend_constants.c

+3-1
Original file line numberDiff line numberDiff line change
@@ -362,8 +362,10 @@ ZEND_API zval *zend_get_class_constant_ex(zend_string *class_name, zend_string *
362362
}
363363

364364
if (UNEXPECTED(ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED)) {
365-
if ((flags & ZEND_FETCH_CLASS_SILENT) == 0) {
365+
if ((flags & ZEND_FETCH_CLASS_SILENT) == 0 && !CONST_IS_RECURSIVE(c)) {
366+
CONST_PROTECT_RECURSION(c);
366367
zend_deprecated_class_constant(c, constant_name);
368+
CONST_UNPROTECT_RECURSION(c);
367369
if (EG(exception)) {
368370
goto failure;
369371
}

Zend/zend_constants.h

+11
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,17 @@
2727
#define CONST_NO_FILE_CACHE (1<<1) /* Can't be saved in file cache */
2828
#define CONST_DEPRECATED (1<<2) /* Deprecated */
2929
#define CONST_OWNED (1<<3) /* constant should be destroyed together with class */
30+
#define CONST_RECURSIVE (1<<4) /* Recursion protection for constant evaluation */
31+
32+
#define CONST_IS_RECURSIVE(c) (Z_CONSTANT_FLAGS((c)->value) & CONST_RECURSIVE)
33+
#define CONST_PROTECT_RECURSION(c) \
34+
do { \
35+
Z_CONSTANT_FLAGS((c)->value) |= CONST_RECURSIVE; \
36+
} while (0)
37+
#define CONST_UNPROTECT_RECURSION(c) \
38+
do { \
39+
Z_CONSTANT_FLAGS((c)->value) &= ~CONST_RECURSIVE; \
40+
} while (0)
3041

3142
#define PHP_USER_CONSTANT 0x7fffff /* a constant defined in user space */
3243

Zend/zend_vm_def.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -6103,8 +6103,10 @@ ZEND_VM_HANDLER(181, ZEND_FETCH_CLASS_CONSTANT, VAR|CONST|UNUSED|CLASS_FETCH, CO
61036103
}
61046104

61056105
bool is_constant_deprecated = ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED;
6106-
if (UNEXPECTED(is_constant_deprecated)) {
6106+
if (UNEXPECTED(is_constant_deprecated) && !CONST_IS_RECURSIVE(c)) {
6107+
CONST_PROTECT_RECURSION(c);
61076108
zend_deprecated_class_constant(c, constant_name);
6109+
CONST_UNPROTECT_RECURSION(c);
61086110

61096111
if (EG(exception)) {
61106112
ZVAL_UNDEF(EX_VAR(opline->result.var));

Zend/zend_vm_execute.h

+18-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ext/opcache/ZendAccelerator.c

+5
Original file line numberDiff line numberDiff line change
@@ -3864,6 +3864,11 @@ static bool preload_try_resolve_constants(zend_class_entry *ce)
38643864
ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&ce->constants_table, key, c) {
38653865
val = &c->value;
38663866
if (Z_TYPE_P(val) == IS_CONSTANT_AST) {
3867+
/* For deprecated constants, we need to flag the zval for recursion
3868+
* detection. Make sure the zval is separated out of shm. */
3869+
if (ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED) {
3870+
ok = false;
3871+
}
38673872
if (EXPECTED(zend_update_class_constant(c, key, c->ce) == SUCCESS)) {
38683873
was_changed = changed = true;
38693874
} else {

0 commit comments

Comments
 (0)