Skip to content

Fix GH-10709: Recursive class constant evaluation #10712

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Zend/tests/array_unpack/classes.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,4 @@ array(3) {
[2]=>
int(3)
}
Exception: Cannot declare self-referencing constant self::B
Exception: Cannot declare self-referencing constant D::A
2 changes: 1 addition & 1 deletion Zend/tests/bug41633_3.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class Foo {
echo Foo::A;
?>
--EXPECTF--
Fatal error: Uncaught Error: Cannot declare self-referencing constant Foo::B in %s:%d
Fatal error: Uncaught Error: Cannot declare self-referencing constant Foo::A in %s:%d
Stack trace:
#0 {main}
thrown in %sbug41633_3.php on line %d
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class A {
var_dump(A::FOO);
?>
--EXPECTF--
Fatal error: Uncaught Error: Cannot declare self-referencing constant self::BAR in %s:%d
Fatal error: Uncaught Error: Cannot declare self-referencing constant A::FOO in %s:%d
Stack trace:
#0 {main}
thrown in %s on line %d
21 changes: 21 additions & 0 deletions Zend/tests/gh10709.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
--TEST--
GH-10634: Fix recursive class constant evaluation
--FILE--
<?php

class B { const HW = A::HW . " extended by B"; }

spl_autoload_register(function ($class) {
class A { const HW = "this is A"; }
var_dump(B::HW);
});

try {
var_dump(new B());
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

?>
--EXPECT--
Cannot declare self-referencing constant B::HW
2 changes: 1 addition & 1 deletion Zend/zend_API.c
Original file line number Diff line number Diff line change
Expand Up @@ -1429,7 +1429,7 @@ ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type) /
}

val = &c->value;
if (UNEXPECTED(zval_update_constant_ex(val, c->ce) != SUCCESS)) {
if (UNEXPECTED(zend_update_class_constant(val, name, c->ce) != SUCCESS)) {
return FAILURE;
}
}
Expand Down
32 changes: 19 additions & 13 deletions Zend/zend_constants.c
Original file line number Diff line number Diff line change
Expand Up @@ -380,19 +380,7 @@ ZEND_API zval *zend_get_class_constant_ex(zend_string *class_name, zend_string *
}

if (ret_constant && Z_TYPE_P(ret_constant) == IS_CONSTANT_AST) {
zend_result ret;

if (IS_CONSTANT_VISITED(ret_constant)) {
zend_throw_error(NULL, "Cannot declare self-referencing constant %s::%s", ZSTR_VAL(class_name), ZSTR_VAL(constant_name));
ret_constant = NULL;
goto failure;
}

MARK_CONSTANT_VISITED(ret_constant);
ret = zval_update_constant_ex(ret_constant, c->ce);
RESET_CONSTANT_VISITED(ret_constant);

if (UNEXPECTED(ret != SUCCESS)) {
if (UNEXPECTED(zend_update_class_constant(ret_constant, constant_name, c->ce) != SUCCESS)) {
ret_constant = NULL;
goto failure;
}
Expand All @@ -401,6 +389,24 @@ ZEND_API zval *zend_get_class_constant_ex(zend_string *class_name, zend_string *
return ret_constant;
}

ZEND_API zend_result zend_update_class_constant(zval *const_zv, zend_string *constant_name, zend_class_entry *ce)
{
if (Z_TYPE_P(const_zv) != IS_CONSTANT_AST) {
return SUCCESS;
}

if (IS_CONSTANT_VISITED(const_zv)) {
zend_throw_error(NULL, "Cannot declare self-referencing constant %s::%s", ZSTR_VAL(ce->name), ZSTR_VAL(constant_name));
return FAILURE;
}

MARK_CONSTANT_VISITED(const_zv);
zend_result result = zval_update_constant_ex(const_zv, ce);
RESET_CONSTANT_VISITED(const_zv);

return result;
}

ZEND_API zval *zend_get_constant_ex(zend_string *cname, zend_class_entry *scope, uint32_t flags)
{
zend_constant *c;
Expand Down
1 change: 1 addition & 0 deletions Zend/zend_constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ ZEND_API zval *zend_get_constant(zend_string *name);
ZEND_API zval *zend_get_constant_str(const char *name, size_t name_len);
ZEND_API zval *zend_get_constant_ex(zend_string *name, zend_class_entry *scope, uint32_t flags);
ZEND_API zval *zend_get_class_constant_ex(zend_string *class_name, zend_string *constant_name, zend_class_entry *scope, uint32_t flags);
ZEND_API zend_result zend_update_class_constant(zval *const_zv, zend_string *constant_name, zend_class_entry *ce);
ZEND_API void zend_register_bool_constant(const char *name, size_t name_len, bool bval, int flags, int module_number);
ZEND_API void zend_register_null_constant(const char *name, size_t name_len, int flags, int module_number);
ZEND_API void zend_register_long_constant(const char *name, size_t name_len, zend_long lval, int flags, int module_number);
Expand Down
2 changes: 1 addition & 1 deletion Zend/zend_vm_def.h
Original file line number Diff line number Diff line change
Expand Up @@ -5914,7 +5914,7 @@ ZEND_VM_HANDLER(181, ZEND_FETCH_CLASS_CONSTANT, VAR|CONST|UNUSED|CLASS_FETCH, CO
}
value = &c->value;
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
zval_update_constant_ex(value, c->ce);
zend_update_class_constant(value, Z_STR_P(RT_CONSTANT(opline, opline->op2)), c->ce);
if (UNEXPECTED(EG(exception) != NULL)) {
ZVAL_UNDEF(EX_VAR(opline->result.var));
HANDLE_EXCEPTION();
Expand Down
6 changes: 3 additions & 3 deletions Zend/zend_vm_execute.h
Original file line number Diff line number Diff line change
Expand Up @@ -7089,7 +7089,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_CONS
}
value = &c->value;
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
zval_update_constant_ex(value, c->ce);
zend_update_class_constant(value, Z_STR_P(RT_CONSTANT(opline, opline->op2)), c->ce);
if (UNEXPECTED(EG(exception) != NULL)) {
ZVAL_UNDEF(EX_VAR(opline->result.var));
HANDLE_EXCEPTION();
Expand Down Expand Up @@ -24597,7 +24597,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_VAR_
}
value = &c->value;
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
zval_update_constant_ex(value, c->ce);
zend_update_class_constant(value, Z_STR_P(RT_CONSTANT(opline, opline->op2)), c->ce);
if (UNEXPECTED(EG(exception) != NULL)) {
ZVAL_UNDEF(EX_VAR(opline->result.var));
HANDLE_EXCEPTION();
Expand Down Expand Up @@ -33382,7 +33382,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUS
}
value = &c->value;
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
zval_update_constant_ex(value, c->ce);
zend_update_class_constant(value, Z_STR_P(RT_CONSTANT(opline, opline->op2)), c->ce);
if (UNEXPECTED(EG(exception) != NULL)) {
ZVAL_UNDEF(EX_VAR(opline->result.var));
HANDLE_EXCEPTION();
Expand Down
5 changes: 3 additions & 2 deletions ext/opcache/ZendAccelerator.c
Original file line number Diff line number Diff line change
Expand Up @@ -3742,10 +3742,11 @@ static bool preload_try_resolve_constants(zend_class_entry *ce)
do {
ok = 1;
changed = 0;
ZEND_HASH_FOREACH_PTR(&ce->constants_table, c) {
zend_string *const_name;
ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->constants_table, const_name, c) {
val = &c->value;
if (Z_TYPE_P(val) == IS_CONSTANT_AST) {
if (EXPECTED(zval_update_constant_ex(val, c->ce) == SUCCESS)) {
if (EXPECTED(zend_update_class_constant(val, const_name, c->ce) == SUCCESS)) {
was_changed = changed = 1;
} else {
ok = 0;
Expand Down
24 changes: 14 additions & 10 deletions ext/reflection/php_reflection.c
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ static zval *reflection_instantiate(zend_class_entry *pce, zval *object) /* {{{
static void _const_string(smart_str *str, char *name, zval *value, char *indent);
static void _function_string(smart_str *str, zend_function *fptr, zend_class_entry *scope, char* indent);
static void _property_string(smart_str *str, zend_property_info *prop, const char *prop_name, char* indent);
static void _class_const_string(smart_str *str, char *name, zend_class_constant *c, char* indent);
static void _class_const_string(smart_str *str, zend_string *name, zend_class_constant *c, char* indent);
static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, char *indent);
static void _extension_string(smart_str *str, zend_module_entry *module, char *indent);
static void _zend_extension_string(smart_str *str, zend_extension *extension, char *indent);
Expand Down Expand Up @@ -385,7 +385,7 @@ static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, char
zend_class_constant *c;

ZEND_HASH_FOREACH_STR_KEY_PTR(CE_CONSTANTS_TABLE(ce), key, c) {
_class_const_string(str, ZSTR_VAL(key), c, ZSTR_VAL(sub_indent));
_class_const_string(str, key, c, ZSTR_VAL(sub_indent));
if (UNEXPECTED(EG(exception))) {
zend_string_release(sub_indent);
return;
Expand Down Expand Up @@ -557,17 +557,17 @@ static void _const_string(smart_str *str, char *name, zval *value, char *indent)
/* }}} */

/* {{{ _class_const_string */
static void _class_const_string(smart_str *str, char *name, zend_class_constant *c, char *indent)
static void _class_const_string(smart_str *str, zend_string *name, zend_class_constant *c, char *indent)
{
if (zval_update_constant_ex(&c->value, c->ce) == FAILURE) {
if (zend_update_class_constant(&c->value, name, c->ce) == FAILURE) {
return;
}

const char *visibility = zend_visibility_string(ZEND_CLASS_CONST_FLAGS(c));
const char *final = ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_FINAL ? "final " : "";
const char *type = zend_zval_type_name(&c->value);
smart_str_append_printf(str, "%sConstant [ %s%s %s %s ] { ",
indent, final, visibility, type, name);
indent, final, visibility, type, ZSTR_VAL(name));
if (Z_TYPE(c->value) == IS_ARRAY) {
smart_str_appends(str, "Array");
} else if (Z_TYPE(c->value) == IS_OBJECT) {
Expand Down Expand Up @@ -3780,7 +3780,7 @@ ZEND_METHOD(ReflectionClassConstant, __toString)
ZVAL_DEREF(name);
ZEND_ASSERT(Z_TYPE_P(name) == IS_STRING);

_class_const_string(&str, Z_STRVAL_P(name), ref, "");
_class_const_string(&str, Z_STR_P(name), ref, "");
RETURN_STR(smart_str_extract(&str));
}
/* }}} */
Expand Down Expand Up @@ -3872,7 +3872,10 @@ ZEND_METHOD(ReflectionClassConstant, getValue)
GET_REFLECTION_OBJECT_PTR(ref);

if (Z_TYPE(ref->value) == IS_CONSTANT_AST) {
zval_update_constant_ex(&ref->value, ref->ce);
zval *name = reflection_prop_name(ZEND_THIS);
// FIXME: Can this be IS_UNDEF?
ZEND_ASSERT(Z_TYPE_P(name) == IS_STRING);
zend_update_class_constant(&ref->value, Z_STR_P(name), ref->ce);
}
ZVAL_COPY_OR_DUP(return_value, &ref->value);
}
Expand Down Expand Up @@ -4678,7 +4681,7 @@ ZEND_METHOD(ReflectionClass, getConstants)

array_init(return_value);
ZEND_HASH_FOREACH_STR_KEY_PTR(CE_CONSTANTS_TABLE(ce), key, constant) {
if (UNEXPECTED(zval_update_constant_ex(&constant->value, constant->ce) != SUCCESS)) {
if (UNEXPECTED(zend_update_class_constant(&constant->value, key, constant->ce) != SUCCESS)) {
RETURN_THROWS();
}

Expand Down Expand Up @@ -4736,8 +4739,9 @@ ZEND_METHOD(ReflectionClass, getConstant)

GET_REFLECTION_OBJECT_PTR(ce);
constants_table = CE_CONSTANTS_TABLE(ce);
ZEND_HASH_FOREACH_PTR(constants_table, c) {
if (UNEXPECTED(zval_update_constant_ex(&c->value, c->ce) != SUCCESS)) {
zend_string *const_name;
ZEND_HASH_FOREACH_STR_KEY_PTR(constants_table, const_name, c) {
if (UNEXPECTED(zend_update_class_constant(&c->value, const_name, c->ce) != SUCCESS)) {
RETURN_THROWS();
}
} ZEND_HASH_FOREACH_END();
Expand Down
2 changes: 1 addition & 1 deletion ext/standard/basic_functions.c
Original file line number Diff line number Diff line change
Expand Up @@ -596,7 +596,7 @@ PHP_FUNCTION(constant)

ZVAL_COPY_OR_DUP(return_value, c);
if (Z_TYPE_P(return_value) == IS_CONSTANT_AST) {
if (UNEXPECTED(zval_update_constant_ex(return_value, scope) != SUCCESS)) {
if (UNEXPECTED(zend_update_class_constant(return_value, const_name, scope) != SUCCESS)) {
RETURN_THROWS();
}
}
Expand Down