From f78d1d0d10284b3599a099b783b56f16b338f728 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Sat, 12 Aug 2023 16:11:29 +0200 Subject: [PATCH] Fix segfault in format_default_value due to unexpected enum/object Evaluating constants at comptime can result in arrays that contain objects. This is problematic for printing the default value of constant ASTs containing objects, because we don't actually know what the constructor arguments were. Avoid this by not propagating array constants. Fixes GH-11937 Closes GH-11947 --- NEWS | 3 +++ Zend/zend_compile.c | 40 +++++++++++++++++++++++++++-- ext/reflection/php_reflection.c | 1 + ext/reflection/tests/gh11937_1.inc | 13 ++++++++++ ext/reflection/tests/gh11937_1.phpt | 30 ++++++++++++++++++++++ ext/reflection/tests/gh11937_2.inc | 4 +++ ext/reflection/tests/gh11937_2.phpt | 27 +++++++++++++++++++ 7 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 ext/reflection/tests/gh11937_1.inc create mode 100644 ext/reflection/tests/gh11937_1.phpt create mode 100644 ext/reflection/tests/gh11937_2.inc create mode 100644 ext/reflection/tests/gh11937_2.phpt diff --git a/NEWS b/NEWS index 0d2bd565067..010acdaf17c 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.1.24 +- Core: + . Fixed bug GH-11937 (Constant ASTs containing objects). (ilutov) + - MySQLnd: . Fixed bug GH-10270 (Invalid error message when connection via SSL fails: "trying to connect via (null)"). (Kamil Tekiela) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index af9be18d10f..351c2708bf4 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1458,6 +1458,35 @@ ZEND_API zend_result zend_unmangle_property_name_ex(const zend_string *name, con } /* }}} */ +static bool array_is_const_ex(zend_array *array, uint32_t *max_checks) +{ + if (zend_hash_num_elements(array) > *max_checks) { + return false; + } + *max_checks -= zend_hash_num_elements(array); + + zval *element; + ZEND_HASH_FOREACH_VAL(array, element) { + if (Z_TYPE_P(element) < IS_ARRAY) { + continue; + } else if (Z_TYPE_P(element) == IS_ARRAY) { + if (!array_is_const_ex(array, max_checks)) { + return false; + } + } else if (UNEXPECTED(Z_TYPE_P(element) >=IS_OBJECT)) { + return false; + } + } ZEND_HASH_FOREACH_END(); + + return true; +} + +static bool array_is_const(zend_array *array) +{ + uint32_t max_checks = 50; + return array_is_const_ex(array, &max_checks); +} + static bool can_ct_eval_const(zend_constant *c) { if (ZEND_CONSTANT_FLAGS(c) & CONST_DEPRECATED) { return 0; @@ -1468,9 +1497,13 @@ static bool can_ct_eval_const(zend_constant *c) { && (CG(compiler_options) & ZEND_COMPILE_WITH_FILE_CACHE))) { return 1; } - if (Z_TYPE(c->value) < IS_OBJECT + if (Z_TYPE(c->value) < IS_ARRAY && !(CG(compiler_options) & ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION)) { return 1; + } else if (Z_TYPE(c->value) == IS_ARRAY + && !(CG(compiler_options) & ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION) + && array_is_const(Z_ARR(c->value))) { + return 1; } return 0; } @@ -1690,7 +1723,10 @@ static bool zend_try_ct_eval_class_const(zval *zv, zend_string *class_name, zend c = &cc->value; /* Substitute case-sensitive (or lowercase) persistent class constants */ - if (Z_TYPE_P(c) < IS_OBJECT) { + if (Z_TYPE_P(c) < IS_ARRAY) { + ZVAL_COPY_OR_DUP(zv, c); + return 1; + } else if (Z_TYPE_P(c) == IS_ARRAY && array_is_const(Z_ARR_P(c))) { ZVAL_COPY_OR_DUP(zv, c); return 1; } diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index d3d5914866a..876338f4ab1 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -639,6 +639,7 @@ static int format_default_value(smart_str *str, zval *value) { } ZEND_HASH_FOREACH_END(); smart_str_appendc(str, ']'); } else if (Z_TYPE_P(value) == IS_OBJECT) { + /* This branch may only be reached for default properties, which don't support arbitrary objects. */ zend_object *obj = Z_OBJ_P(value); zend_class_entry *class = obj->ce; ZEND_ASSERT(class->ce_flags & ZEND_ACC_ENUM); diff --git a/ext/reflection/tests/gh11937_1.inc b/ext/reflection/tests/gh11937_1.inc new file mode 100644 index 00000000000..4d55213f2f8 --- /dev/null +++ b/ext/reflection/tests/gh11937_1.inc @@ -0,0 +1,13 @@ +getAttributes('Attr')[0]; + +?> +--EXPECT-- +array(2) { + [0]=> + enum(TestEnum::One) + [1]=> + enum(TestEnum::Two) +} +Attribute [ Attr ] { + - Arguments [1] { + Argument #0 [ new \Foo(TestEnum::CASES) ] + } +} diff --git a/ext/reflection/tests/gh11937_2.inc b/ext/reflection/tests/gh11937_2.inc new file mode 100644 index 00000000000..d6e21e6ba5c --- /dev/null +++ b/ext/reflection/tests/gh11937_2.inc @@ -0,0 +1,4 @@ +getAttributes('Attr')[0]; + +?> +--EXPECT-- +array(1) { + [0]=> + object(Foo)#1 (0) { + } +} +Attribute [ Attr ] { + - Arguments [1] { + Argument #0 [ FOOS ] + } +}