Skip to content

Commit cd99309

Browse files
committed
Fix GH-10709: Recursive class constant evaluation
Fixes https://oss-fuzz.com/testcase-detail/6445949468934144
1 parent b09be29 commit cd99309

File tree

12 files changed

+67
-34
lines changed

12 files changed

+67
-34
lines changed

Zend/tests/array_unpack/classes.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,4 @@ array(3) {
4444
[2]=>
4545
int(3)
4646
}
47-
Exception: Cannot declare self-referencing constant self::B
47+
Exception: Cannot declare self-referencing constant D::A

Zend/tests/bug41633_3.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ class Foo {
99
echo Foo::A;
1010
?>
1111
--EXPECTF--
12-
Fatal error: Uncaught Error: Cannot declare self-referencing constant Foo::B in %s:%d
12+
Fatal error: Uncaught Error: Cannot declare self-referencing constant Foo::A in %s:%d
1313
Stack trace:
1414
#0 {main}
1515
thrown in %sbug41633_3.php on line %d

Zend/tests/constant_expressions_self_referencing_array.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ class A {
99
var_dump(A::FOO);
1010
?>
1111
--EXPECTF--
12-
Fatal error: Uncaught Error: Cannot declare self-referencing constant self::BAR in %s:%d
12+
Fatal error: Uncaught Error: Cannot declare self-referencing constant A::FOO in %s:%d
1313
Stack trace:
1414
#0 {main}
1515
thrown in %s on line %d

Zend/tests/gh10709.phpt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
--TEST--
2+
GH-10634: Test
3+
--FILE--
4+
<?php
5+
6+
class B { const HW = A::HW . " extended by B"; }
7+
8+
spl_autoload_register(function ($class) {
9+
class A { const HW = "this is A"; }
10+
var_dump(B::HW);
11+
});
12+
13+
try {
14+
var_dump(new B());
15+
} catch (Error $e) {
16+
echo $e->getMessage(), "\n";
17+
}
18+
19+
?>
20+
--EXPECT--
21+
Cannot declare self-referencing constant B::HW

Zend/zend_API.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1429,7 +1429,7 @@ ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type) /
14291429
}
14301430

14311431
val = &c->value;
1432-
if (UNEXPECTED(zval_update_constant_ex(val, c->ce) != SUCCESS)) {
1432+
if (UNEXPECTED(zend_update_class_constant(val, name, c->ce) != SUCCESS)) {
14331433
return FAILURE;
14341434
}
14351435
}

Zend/zend_constants.c

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -380,19 +380,7 @@ ZEND_API zval *zend_get_class_constant_ex(zend_string *class_name, zend_string *
380380
}
381381

382382
if (ret_constant && Z_TYPE_P(ret_constant) == IS_CONSTANT_AST) {
383-
zend_result ret;
384-
385-
if (IS_CONSTANT_VISITED(ret_constant)) {
386-
zend_throw_error(NULL, "Cannot declare self-referencing constant %s::%s", ZSTR_VAL(class_name), ZSTR_VAL(constant_name));
387-
ret_constant = NULL;
388-
goto failure;
389-
}
390-
391-
MARK_CONSTANT_VISITED(ret_constant);
392-
ret = zval_update_constant_ex(ret_constant, c->ce);
393-
RESET_CONSTANT_VISITED(ret_constant);
394-
395-
if (UNEXPECTED(ret != SUCCESS)) {
383+
if (UNEXPECTED(zend_update_class_constant(ret_constant, constant_name, c->ce) != SUCCESS)) {
396384
ret_constant = NULL;
397385
goto failure;
398386
}
@@ -401,6 +389,24 @@ ZEND_API zval *zend_get_class_constant_ex(zend_string *class_name, zend_string *
401389
return ret_constant;
402390
}
403391

392+
ZEND_API zend_result zend_update_class_constant(zval *const_zv, zend_string *constant_name, zend_class_entry *ce)
393+
{
394+
if (Z_TYPE_P(const_zv) != IS_CONSTANT_AST) {
395+
return SUCCESS;
396+
}
397+
398+
if (IS_CONSTANT_VISITED(const_zv)) {
399+
zend_throw_error(NULL, "Cannot declare self-referencing constant %s::%s", ZSTR_VAL(ce->name), ZSTR_VAL(constant_name));
400+
return FAILURE;
401+
}
402+
403+
MARK_CONSTANT_VISITED(const_zv);
404+
zend_result result = zval_update_constant_ex(const_zv, ce);
405+
RESET_CONSTANT_VISITED(const_zv);
406+
407+
return result;
408+
}
409+
404410
ZEND_API zval *zend_get_constant_ex(zend_string *cname, zend_class_entry *scope, uint32_t flags)
405411
{
406412
zend_constant *c;

Zend/zend_constants.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ ZEND_API zval *zend_get_constant(zend_string *name);
7878
ZEND_API zval *zend_get_constant_str(const char *name, size_t name_len);
7979
ZEND_API zval *zend_get_constant_ex(zend_string *name, zend_class_entry *scope, uint32_t flags);
8080
ZEND_API zval *zend_get_class_constant_ex(zend_string *class_name, zend_string *constant_name, zend_class_entry *scope, uint32_t flags);
81+
ZEND_API zend_result zend_update_class_constant(zval *const_zv, zend_string *constant_name, zend_class_entry *ce);
8182
ZEND_API void zend_register_bool_constant(const char *name, size_t name_len, bool bval, int flags, int module_number);
8283
ZEND_API void zend_register_null_constant(const char *name, size_t name_len, int flags, int module_number);
8384
ZEND_API void zend_register_long_constant(const char *name, size_t name_len, zend_long lval, int flags, int module_number);

Zend/zend_vm_def.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5914,7 +5914,7 @@ ZEND_VM_HANDLER(181, ZEND_FETCH_CLASS_CONSTANT, VAR|CONST|UNUSED|CLASS_FETCH, CO
59145914
}
59155915
value = &c->value;
59165916
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
5917-
zval_update_constant_ex(value, c->ce);
5917+
zend_update_class_constant(value, Z_STR_P(RT_CONSTANT(opline, opline->op2)), c->ce);
59185918
if (UNEXPECTED(EG(exception) != NULL)) {
59195919
ZVAL_UNDEF(EX_VAR(opline->result.var));
59205920
HANDLE_EXCEPTION();

Zend/zend_vm_execute.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7089,7 +7089,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_CONS
70897089
}
70907090
value = &c->value;
70917091
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
7092-
zval_update_constant_ex(value, c->ce);
7092+
zend_update_class_constant(value, Z_STR_P(RT_CONSTANT(opline, opline->op2)), c->ce);
70937093
if (UNEXPECTED(EG(exception) != NULL)) {
70947094
ZVAL_UNDEF(EX_VAR(opline->result.var));
70957095
HANDLE_EXCEPTION();
@@ -24597,7 +24597,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_VAR_
2459724597
}
2459824598
value = &c->value;
2459924599
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
24600-
zval_update_constant_ex(value, c->ce);
24600+
zend_update_class_constant(value, Z_STR_P(RT_CONSTANT(opline, opline->op2)), c->ce);
2460124601
if (UNEXPECTED(EG(exception) != NULL)) {
2460224602
ZVAL_UNDEF(EX_VAR(opline->result.var));
2460324603
HANDLE_EXCEPTION();
@@ -33382,7 +33382,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUS
3338233382
}
3338333383
value = &c->value;
3338433384
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
33385-
zval_update_constant_ex(value, c->ce);
33385+
zend_update_class_constant(value, Z_STR_P(RT_CONSTANT(opline, opline->op2)), c->ce);
3338633386
if (UNEXPECTED(EG(exception) != NULL)) {
3338733387
ZVAL_UNDEF(EX_VAR(opline->result.var));
3338833388
HANDLE_EXCEPTION();

ext/opcache/ZendAccelerator.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3742,10 +3742,11 @@ static bool preload_try_resolve_constants(zend_class_entry *ce)
37423742
do {
37433743
ok = 1;
37443744
changed = 0;
3745-
ZEND_HASH_FOREACH_PTR(&ce->constants_table, c) {
3745+
zend_string *const_name;
3746+
ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->constants_table, const_name, c) {
37463747
val = &c->value;
37473748
if (Z_TYPE_P(val) == IS_CONSTANT_AST) {
3748-
if (EXPECTED(zval_update_constant_ex(val, c->ce) == SUCCESS)) {
3749+
if (EXPECTED(zend_update_class_constant(val, const_name, c->ce) == SUCCESS)) {
37493750
was_changed = changed = 1;
37503751
} else {
37513752
ok = 0;

ext/reflection/php_reflection.c

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ static zval *reflection_instantiate(zend_class_entry *pce, zval *object) /* {{{
301301
static void _const_string(smart_str *str, char *name, zval *value, char *indent);
302302
static void _function_string(smart_str *str, zend_function *fptr, zend_class_entry *scope, char* indent);
303303
static void _property_string(smart_str *str, zend_property_info *prop, const char *prop_name, char* indent);
304-
static void _class_const_string(smart_str *str, char *name, zend_class_constant *c, char* indent);
304+
static void _class_const_string(smart_str *str, zend_string *name, zend_class_constant *c, char* indent);
305305
static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, char *indent);
306306
static void _extension_string(smart_str *str, zend_module_entry *module, char *indent);
307307
static void _zend_extension_string(smart_str *str, zend_extension *extension, char *indent);
@@ -385,7 +385,7 @@ static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, char
385385
zend_class_constant *c;
386386

387387
ZEND_HASH_FOREACH_STR_KEY_PTR(CE_CONSTANTS_TABLE(ce), key, c) {
388-
_class_const_string(str, ZSTR_VAL(key), c, ZSTR_VAL(sub_indent));
388+
_class_const_string(str, key, c, ZSTR_VAL(sub_indent));
389389
if (UNEXPECTED(EG(exception))) {
390390
zend_string_release(sub_indent);
391391
return;
@@ -557,17 +557,17 @@ static void _const_string(smart_str *str, char *name, zval *value, char *indent)
557557
/* }}} */
558558

559559
/* {{{ _class_const_string */
560-
static void _class_const_string(smart_str *str, char *name, zend_class_constant *c, char *indent)
560+
static void _class_const_string(smart_str *str, zend_string *name, zend_class_constant *c, char *indent)
561561
{
562-
if (zval_update_constant_ex(&c->value, c->ce) == FAILURE) {
562+
if (zend_update_class_constant(&c->value, name, c->ce) == FAILURE) {
563563
return;
564564
}
565565

566566
const char *visibility = zend_visibility_string(ZEND_CLASS_CONST_FLAGS(c));
567567
const char *final = ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_FINAL ? "final " : "";
568568
const char *type = zend_zval_type_name(&c->value);
569569
smart_str_append_printf(str, "%sConstant [ %s%s %s %s ] { ",
570-
indent, final, visibility, type, name);
570+
indent, final, visibility, type, ZSTR_VAL(name));
571571
if (Z_TYPE(c->value) == IS_ARRAY) {
572572
smart_str_appends(str, "Array");
573573
} else if (Z_TYPE(c->value) == IS_OBJECT) {
@@ -3780,7 +3780,7 @@ ZEND_METHOD(ReflectionClassConstant, __toString)
37803780
ZVAL_DEREF(name);
37813781
ZEND_ASSERT(Z_TYPE_P(name) == IS_STRING);
37823782

3783-
_class_const_string(&str, Z_STRVAL_P(name), ref, "");
3783+
_class_const_string(&str, Z_STR_P(name), ref, "");
37843784
RETURN_STR(smart_str_extract(&str));
37853785
}
37863786
/* }}} */
@@ -3872,7 +3872,10 @@ ZEND_METHOD(ReflectionClassConstant, getValue)
38723872
GET_REFLECTION_OBJECT_PTR(ref);
38733873

38743874
if (Z_TYPE(ref->value) == IS_CONSTANT_AST) {
3875-
zval_update_constant_ex(&ref->value, ref->ce);
3875+
zval *name = reflection_prop_name(ZEND_THIS);
3876+
// FIXME: Can this be IS_UNDEF?
3877+
ZEND_ASSERT(Z_TYPE_P(name) == IS_STRING);
3878+
zend_update_class_constant(&ref->value, Z_STR_P(name), ref->ce);
38763879
}
38773880
ZVAL_COPY_OR_DUP(return_value, &ref->value);
38783881
}
@@ -4678,7 +4681,7 @@ ZEND_METHOD(ReflectionClass, getConstants)
46784681

46794682
array_init(return_value);
46804683
ZEND_HASH_FOREACH_STR_KEY_PTR(CE_CONSTANTS_TABLE(ce), key, constant) {
4681-
if (UNEXPECTED(zval_update_constant_ex(&constant->value, constant->ce) != SUCCESS)) {
4684+
if (UNEXPECTED(zend_update_class_constant(&constant->value, key, constant->ce) != SUCCESS)) {
46824685
RETURN_THROWS();
46834686
}
46844687

@@ -4736,8 +4739,9 @@ ZEND_METHOD(ReflectionClass, getConstant)
47364739

47374740
GET_REFLECTION_OBJECT_PTR(ce);
47384741
constants_table = CE_CONSTANTS_TABLE(ce);
4739-
ZEND_HASH_FOREACH_PTR(constants_table, c) {
4740-
if (UNEXPECTED(zval_update_constant_ex(&c->value, c->ce) != SUCCESS)) {
4742+
zend_string *const_name;
4743+
ZEND_HASH_FOREACH_STR_KEY_PTR(constants_table, const_name, c) {
4744+
if (UNEXPECTED(zend_update_class_constant(&c->value, const_name, c->ce) != SUCCESS)) {
47414745
RETURN_THROWS();
47424746
}
47434747
} ZEND_HASH_FOREACH_END();

ext/standard/basic_functions.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -596,7 +596,7 @@ PHP_FUNCTION(constant)
596596

597597
ZVAL_COPY_OR_DUP(return_value, c);
598598
if (Z_TYPE_P(return_value) == IS_CONSTANT_AST) {
599-
if (UNEXPECTED(zval_update_constant_ex(return_value, scope) != SUCCESS)) {
599+
if (UNEXPECTED(zend_update_class_constant(return_value, const_name, scope) != SUCCESS)) {
600600
RETURN_THROWS();
601601
}
602602
}

0 commit comments

Comments
 (0)