From 3a918d6cc6dd0d51ca09202ff098d7dd55ccec55 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Sat, 12 Aug 2023 16:11:29 +0200 Subject: [PATCH 1/2] 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 --- Zend/zend_compile.c | 4 ++-- 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 ++++++++++++++++++++++++++ 6 files changed, 77 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/Zend/zend_compile.c b/Zend/zend_compile.c index af9be18d10f5b..9dafeb9a05ba8 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1468,7 +1468,7 @@ 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; } @@ -1690,7 +1690,7 @@ 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; } diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index d3d5914866a1e..876338f4ab127 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 0000000000000..4d55213f2f831 --- /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 0000000000000..d6e21e6ba5c55 --- /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 ] + } +} From e9951cd43284a3e9e48df5c166dd4d76e9527e3a Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Wed, 16 Aug 2023 14:24:10 +0200 Subject: [PATCH 2/2] Check if array is CT at comp time --- Zend/zend_compile.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 9dafeb9a05ba8..25e6337b4a64b 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1458,6 +1458,33 @@ 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) { + 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; @@ -1471,6 +1498,10 @@ static bool can_ct_eval_const(zend_constant *c) { 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; } @@ -1693,6 +1724,9 @@ static bool zend_try_ct_eval_class_const(zval *zv, zend_string *class_name, zend 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; } return 0;