mirror of
https://github.com/php/php-src.git
synced 2025-08-15 21:48:51 +02:00
Reapply GH-17712 with a fix for internal class constants (#18464)
Add recursion protection when emitting deprecation warnings for class constants, since the deprecation message can come from an attribute that is using the same constant for the message, or otherwise result in recursion. But, internal constants are persisted, and thus cannot have recursion protection. Otherwise, if a user error handler triggers bailout before the recursion flag is removed then a subsequent request (e.g. with `--repeat 2`) would start with that flag already applied. Internal constants can presumably be trusted not to use deprecation messages that come from recursive attributes. Fixes GH-18463 Fixes GH-17711
This commit is contained in:
parent
78f03cd5f2
commit
cd751f98cb
10 changed files with 138 additions and 9 deletions
5
NEWS
5
NEWS
|
@ -6,6 +6,11 @@ PHP NEWS
|
|||
. Fixed bug GH-18641 (Accessing a BcMath\Number property by ref crashes).
|
||||
(nielsdos)
|
||||
|
||||
- Core:
|
||||
. Fixed bugs GH-17711 and GH-18022 (Infinite recursion on deprecated attribute
|
||||
evaluation) and GH-18464 (Recursion protection for deprecation constants not
|
||||
released on bailout). (DanielEScherzer and ilutov)
|
||||
|
||||
- Intl:
|
||||
. Fix memory leak in intl_datetime_decompose() on failure. (nielsdos)
|
||||
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
--TEST--
|
||||
GH-17711: Infinite recursion through deprecated class constants self-referencing through deprecation message
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
#[\Deprecated(self::C)]
|
||||
const C = TEST;
|
||||
}
|
||||
|
||||
const TEST = 'Message';
|
||||
var_dump(C::C);
|
||||
|
||||
class D {
|
||||
#[\Deprecated(Alias::C)]
|
||||
const C = 'test';
|
||||
}
|
||||
|
||||
class_alias('D', 'Alias');
|
||||
var_dump(D::C);
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Deprecated: Constant C::C is deprecated, Message in %s on line %d
|
||||
string(7) "Message"
|
||||
|
||||
Deprecated: Constant D::C is deprecated, test in %s on line %d
|
||||
string(4) "test"
|
20
Zend/tests/constants/gh18463-class-constant.phpt
Normal file
20
Zend/tests/constants/gh18463-class-constant.phpt
Normal file
|
@ -0,0 +1,20 @@
|
|||
--TEST--
|
||||
GH-18463: Recursion protection should not be applied to internal class constants
|
||||
--EXTENSIONS--
|
||||
zend_test
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
function handler($errno, $errstr, $errfile, $errline) {
|
||||
echo "$errstr in $errfile on line $errline\n";
|
||||
eval('class string {}');
|
||||
}
|
||||
|
||||
set_error_handler('handler');
|
||||
|
||||
var_dump(_ZendTestClass::ZEND_TEST_DEPRECATED);
|
||||
?>
|
||||
--EXPECTF--
|
||||
Constant _ZendTestClass::ZEND_TEST_DEPRECATED is deprecated in %s on line %d
|
||||
|
||||
Fatal error: Cannot use "string" as a class name as it is reserved in %s(%d) : eval()'d code on line %d
|
|
@ -1439,7 +1439,7 @@ ZEND_API HashTable *zend_separate_class_constants_table(zend_class_entry *class_
|
|||
|
||||
ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&class_type->constants_table, key, c) {
|
||||
if (c->ce == class_type) {
|
||||
if (Z_TYPE(c->value) == IS_CONSTANT_AST) {
|
||||
if (Z_TYPE(c->value) == IS_CONSTANT_AST || (ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED)) {
|
||||
new_c = zend_arena_alloc(&CG(arena), sizeof(zend_class_constant));
|
||||
memcpy(new_c, c, sizeof(zend_class_constant));
|
||||
c = new_c;
|
||||
|
|
|
@ -8822,6 +8822,10 @@ static void zend_compile_class_const_decl(zend_ast *ast, uint32_t flags, zend_as
|
|||
|
||||
if (deprecated) {
|
||||
ZEND_CLASS_CONST_FLAGS(c) |= ZEND_ACC_DEPRECATED;
|
||||
/* For deprecated constants, we need to flag the zval for recursion
|
||||
* detection. Make sure the zval is separated out of shm. */
|
||||
ce->ce_flags |= ZEND_ACC_HAS_AST_CONSTANTS;
|
||||
ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -353,8 +353,15 @@ ZEND_API zval *zend_get_class_constant_ex(zend_string *class_name, zend_string *
|
|||
}
|
||||
|
||||
if (UNEXPECTED(ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED)) {
|
||||
if ((flags & ZEND_FETCH_CLASS_SILENT) == 0) {
|
||||
if ((flags & ZEND_FETCH_CLASS_SILENT) == 0 && !CONST_IS_RECURSIVE(c)) {
|
||||
if (c->ce->type == ZEND_USER_CLASS) {
|
||||
/* Recursion protection only applied to user constants, GH-18463 */
|
||||
CONST_PROTECT_RECURSION(c);
|
||||
}
|
||||
zend_deprecated_class_constant(c, constant_name);
|
||||
if (c->ce->type == ZEND_USER_CLASS) {
|
||||
CONST_UNPROTECT_RECURSION(c);
|
||||
}
|
||||
if (EG(exception)) {
|
||||
goto failure;
|
||||
}
|
||||
|
|
|
@ -27,6 +27,17 @@
|
|||
#define CONST_NO_FILE_CACHE (1<<1) /* Can't be saved in file cache */
|
||||
#define CONST_DEPRECATED (1<<2) /* Deprecated */
|
||||
#define CONST_OWNED (1<<3) /* constant should be destroyed together with class */
|
||||
#define CONST_RECURSIVE (1<<4) /* Recursion protection for constant evaluation */
|
||||
|
||||
#define CONST_IS_RECURSIVE(c) (Z_CONSTANT_FLAGS((c)->value) & CONST_RECURSIVE)
|
||||
#define CONST_PROTECT_RECURSION(c) \
|
||||
do { \
|
||||
Z_CONSTANT_FLAGS((c)->value) |= CONST_RECURSIVE; \
|
||||
} while (0)
|
||||
#define CONST_UNPROTECT_RECURSION(c) \
|
||||
do { \
|
||||
Z_CONSTANT_FLAGS((c)->value) &= ~CONST_RECURSIVE; \
|
||||
} while (0)
|
||||
|
||||
#define PHP_USER_CONSTANT 0x7fffff /* a constant defined in user space */
|
||||
|
||||
|
|
|
@ -6094,8 +6094,15 @@ ZEND_VM_HANDLER(181, ZEND_FETCH_CLASS_CONSTANT, VAR|CONST|UNUSED|CLASS_FETCH, CO
|
|||
}
|
||||
|
||||
bool is_constant_deprecated = ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED;
|
||||
if (UNEXPECTED(is_constant_deprecated)) {
|
||||
if (UNEXPECTED(is_constant_deprecated) && !CONST_IS_RECURSIVE(c)) {
|
||||
if (c->ce->type == ZEND_USER_CLASS) {
|
||||
/* Recursion protection only applied to user constants, GH-18463 */
|
||||
CONST_PROTECT_RECURSION(c);
|
||||
}
|
||||
zend_deprecated_class_constant(c, constant_name);
|
||||
if (c->ce->type == ZEND_USER_CLASS) {
|
||||
CONST_UNPROTECT_RECURSION(c);
|
||||
}
|
||||
|
||||
if (EG(exception)) {
|
||||
ZVAL_UNDEF(EX_VAR(opline->result.var));
|
||||
|
|
54
Zend/zend_vm_execute.h
generated
54
Zend/zend_vm_execute.h
generated
|
@ -7615,8 +7615,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_CONS
|
|||
}
|
||||
|
||||
bool is_constant_deprecated = ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED;
|
||||
if (UNEXPECTED(is_constant_deprecated)) {
|
||||
if (UNEXPECTED(is_constant_deprecated) && !CONST_IS_RECURSIVE(c)) {
|
||||
if (c->ce->type == ZEND_USER_CLASS) {
|
||||
/* Recursion protection only applied to user constants, GH-18463 */
|
||||
CONST_PROTECT_RECURSION(c);
|
||||
}
|
||||
zend_deprecated_class_constant(c, constant_name);
|
||||
if (c->ce->type == ZEND_USER_CLASS) {
|
||||
CONST_UNPROTECT_RECURSION(c);
|
||||
}
|
||||
|
||||
if (EG(exception)) {
|
||||
ZVAL_UNDEF(EX_VAR(opline->result.var));
|
||||
|
@ -8776,8 +8783,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_CONS
|
|||
}
|
||||
|
||||
bool is_constant_deprecated = ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED;
|
||||
if (UNEXPECTED(is_constant_deprecated)) {
|
||||
if (UNEXPECTED(is_constant_deprecated) && !CONST_IS_RECURSIVE(c)) {
|
||||
if (c->ce->type == ZEND_USER_CLASS) {
|
||||
/* Recursion protection only applied to user constants, GH-18463 */
|
||||
CONST_PROTECT_RECURSION(c);
|
||||
}
|
||||
zend_deprecated_class_constant(c, constant_name);
|
||||
if (c->ce->type == ZEND_USER_CLASS) {
|
||||
CONST_UNPROTECT_RECURSION(c);
|
||||
}
|
||||
|
||||
if (EG(exception)) {
|
||||
ZVAL_UNDEF(EX_VAR(opline->result.var));
|
||||
|
@ -25882,8 +25896,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_VAR_
|
|||
}
|
||||
|
||||
bool is_constant_deprecated = ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED;
|
||||
if (UNEXPECTED(is_constant_deprecated)) {
|
||||
if (UNEXPECTED(is_constant_deprecated) && !CONST_IS_RECURSIVE(c)) {
|
||||
if (c->ce->type == ZEND_USER_CLASS) {
|
||||
/* Recursion protection only applied to user constants, GH-18463 */
|
||||
CONST_PROTECT_RECURSION(c);
|
||||
}
|
||||
zend_deprecated_class_constant(c, constant_name);
|
||||
if (c->ce->type == ZEND_USER_CLASS) {
|
||||
CONST_UNPROTECT_RECURSION(c);
|
||||
}
|
||||
|
||||
if (EG(exception)) {
|
||||
ZVAL_UNDEF(EX_VAR(opline->result.var));
|
||||
|
@ -26452,8 +26473,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_VAR_
|
|||
}
|
||||
|
||||
bool is_constant_deprecated = ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED;
|
||||
if (UNEXPECTED(is_constant_deprecated)) {
|
||||
if (UNEXPECTED(is_constant_deprecated) && !CONST_IS_RECURSIVE(c)) {
|
||||
if (c->ce->type == ZEND_USER_CLASS) {
|
||||
/* Recursion protection only applied to user constants, GH-18463 */
|
||||
CONST_PROTECT_RECURSION(c);
|
||||
}
|
||||
zend_deprecated_class_constant(c, constant_name);
|
||||
if (c->ce->type == ZEND_USER_CLASS) {
|
||||
CONST_UNPROTECT_RECURSION(c);
|
||||
}
|
||||
|
||||
if (EG(exception)) {
|
||||
ZVAL_UNDEF(EX_VAR(opline->result.var));
|
||||
|
@ -35294,8 +35322,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUS
|
|||
}
|
||||
|
||||
bool is_constant_deprecated = ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED;
|
||||
if (UNEXPECTED(is_constant_deprecated)) {
|
||||
if (UNEXPECTED(is_constant_deprecated) && !CONST_IS_RECURSIVE(c)) {
|
||||
if (c->ce->type == ZEND_USER_CLASS) {
|
||||
/* Recursion protection only applied to user constants, GH-18463 */
|
||||
CONST_PROTECT_RECURSION(c);
|
||||
}
|
||||
zend_deprecated_class_constant(c, constant_name);
|
||||
if (c->ce->type == ZEND_USER_CLASS) {
|
||||
CONST_UNPROTECT_RECURSION(c);
|
||||
}
|
||||
|
||||
if (EG(exception)) {
|
||||
ZVAL_UNDEF(EX_VAR(opline->result.var));
|
||||
|
@ -35654,8 +35689,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUS
|
|||
}
|
||||
|
||||
bool is_constant_deprecated = ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED;
|
||||
if (UNEXPECTED(is_constant_deprecated)) {
|
||||
if (UNEXPECTED(is_constant_deprecated) && !CONST_IS_RECURSIVE(c)) {
|
||||
if (c->ce->type == ZEND_USER_CLASS) {
|
||||
/* Recursion protection only applied to user constants, GH-18463 */
|
||||
CONST_PROTECT_RECURSION(c);
|
||||
}
|
||||
zend_deprecated_class_constant(c, constant_name);
|
||||
if (c->ce->type == ZEND_USER_CLASS) {
|
||||
CONST_UNPROTECT_RECURSION(c);
|
||||
}
|
||||
|
||||
if (EG(exception)) {
|
||||
ZVAL_UNDEF(EX_VAR(opline->result.var));
|
||||
|
|
|
@ -3806,6 +3806,11 @@ static bool preload_try_resolve_constants(zend_class_entry *ce)
|
|||
ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&ce->constants_table, key, c) {
|
||||
val = &c->value;
|
||||
if (Z_TYPE_P(val) == IS_CONSTANT_AST) {
|
||||
/* For deprecated constants, we need to flag the zval for recursion
|
||||
* detection. Make sure the zval is separated out of shm. */
|
||||
if (ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED) {
|
||||
ok = false;
|
||||
}
|
||||
if (EXPECTED(zend_update_class_constant(c, key, c->ce) == SUCCESS)) {
|
||||
was_changed = changed = true;
|
||||
} else {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue