diff --git a/Zend/tests/gh17711.phpt b/Zend/tests/gh17711.phpt new file mode 100644 index 000000000000..f37e8c90b92f --- /dev/null +++ b/Zend/tests/gh17711.phpt @@ -0,0 +1,18 @@ +--TEST-- +GH-17711: Infinite recursion through deprecated class constants self-referencing through deprecation message +--FILE-- + +--EXPECTF-- +Deprecated: Constant C::C is deprecated, Message in %s on line %d +string(7) "Message" diff --git a/Zend/zend_constants.c b/Zend/zend_constants.c index d453b8bb7371..2145cb191535 100644 --- a/Zend/zend_constants.c +++ b/Zend/zend_constants.c @@ -353,8 +353,10 @@ 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)) { + CONST_PROTECT_RECURSION(c); zend_deprecated_class_constant(c, constant_name); + CONST_UNPROTECT_RECURSION(c); if (EG(exception)) { goto failure; } diff --git a/Zend/zend_constants.h b/Zend/zend_constants.h index 6f0710c0ce63..6d83d9428860 100644 --- a/Zend/zend_constants.h +++ b/Zend/zend_constants.h @@ -27,6 +27,21 @@ #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 { \ + if (Z_TYPE((c)->value) == IS_CONSTANT_AST) { \ + Z_CONSTANT_FLAGS((c)->value) |= CONST_RECURSIVE; \ + } \ + } while (0) +#define CONST_UNPROTECT_RECURSION(zval) \ + do { \ + if (Z_TYPE((c)->value) == IS_CONSTANT_AST) { \ + Z_CONSTANT_FLAGS((c)->value) &= ~CONST_RECURSIVE; \ + } \ + } while (0) #define PHP_USER_CONSTANT 0x7fffff /* a constant defined in user space */ diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index eea3a4f7e2da..32ff931fe86f 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -6094,8 +6094,10 @@ 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)) { + CONST_PROTECT_RECURSION(c); zend_deprecated_class_constant(c, constant_name); + CONST_UNPROTECT_RECURSION(c); if (EG(exception)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index cb4f3747a02b..c5d303b22fca 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -7615,8 +7615,10 @@ 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)) { + CONST_PROTECT_RECURSION(c); zend_deprecated_class_constant(c, constant_name); + CONST_UNPROTECT_RECURSION(c); if (EG(exception)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); @@ -8775,8 +8777,10 @@ 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)) { + CONST_PROTECT_RECURSION(c); zend_deprecated_class_constant(c, constant_name); + CONST_UNPROTECT_RECURSION(c); if (EG(exception)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); @@ -25874,8 +25878,10 @@ 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)) { + CONST_PROTECT_RECURSION(c); zend_deprecated_class_constant(c, constant_name); + CONST_UNPROTECT_RECURSION(c); if (EG(exception)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); @@ -26443,8 +26449,10 @@ 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)) { + CONST_PROTECT_RECURSION(c); zend_deprecated_class_constant(c, constant_name); + CONST_UNPROTECT_RECURSION(c); if (EG(exception)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); @@ -35282,8 +35290,10 @@ 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)) { + CONST_PROTECT_RECURSION(c); zend_deprecated_class_constant(c, constant_name); + CONST_UNPROTECT_RECURSION(c); if (EG(exception)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); @@ -35641,8 +35651,10 @@ 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)) { + CONST_PROTECT_RECURSION(c); zend_deprecated_class_constant(c, constant_name); + CONST_UNPROTECT_RECURSION(c); if (EG(exception)) { ZVAL_UNDEF(EX_VAR(opline->result.var));