Skip to content

Commit 48ede64

Browse files
committed
Merge branch 'PHP-8.2'
* PHP-8.2: Fix segfault in format_default_value due to unexpected enum/object
2 parents 7e4ca2e + 9437aa9 commit 48ede64

File tree

7 files changed

+115
-2
lines changed

7 files changed

+115
-2
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ PHP NEWS
22
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
33
?? ??? ????, PHP 8.3.0RC1
44

5+
- Core:
6+
. Fixed bug GH-11937 (Constant ASTs containing objects). (ilutov)
57

68
17 Aug 2023, PHP 8.3.0beta3
79

Zend/zend_compile.c

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1553,6 +1553,35 @@ ZEND_API zend_result zend_unmangle_property_name_ex(const zend_string *name, con
15531553
}
15541554
/* }}} */
15551555

1556+
static bool array_is_const_ex(zend_array *array, uint32_t *max_checks)
1557+
{
1558+
if (zend_hash_num_elements(array) > *max_checks) {
1559+
return false;
1560+
}
1561+
*max_checks -= zend_hash_num_elements(array);
1562+
1563+
zval *element;
1564+
ZEND_HASH_FOREACH_VAL(array, element) {
1565+
if (Z_TYPE_P(element) < IS_ARRAY) {
1566+
continue;
1567+
} else if (Z_TYPE_P(element) == IS_ARRAY) {
1568+
if (!array_is_const_ex(array, max_checks)) {
1569+
return false;
1570+
}
1571+
} else if (UNEXPECTED(Z_TYPE_P(element) >=IS_OBJECT)) {
1572+
return false;
1573+
}
1574+
} ZEND_HASH_FOREACH_END();
1575+
1576+
return true;
1577+
}
1578+
1579+
static bool array_is_const(zend_array *array)
1580+
{
1581+
uint32_t max_checks = 50;
1582+
return array_is_const_ex(array, &max_checks);
1583+
}
1584+
15561585
static bool can_ct_eval_const(zend_constant *c) {
15571586
if (ZEND_CONSTANT_FLAGS(c) & CONST_DEPRECATED) {
15581587
return 0;
@@ -1563,9 +1592,13 @@ static bool can_ct_eval_const(zend_constant *c) {
15631592
&& (CG(compiler_options) & ZEND_COMPILE_WITH_FILE_CACHE))) {
15641593
return 1;
15651594
}
1566-
if (Z_TYPE(c->value) < IS_OBJECT
1595+
if (Z_TYPE(c->value) < IS_ARRAY
15671596
&& !(CG(compiler_options) & ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION)) {
15681597
return 1;
1598+
} else if (Z_TYPE(c->value) == IS_ARRAY
1599+
&& !(CG(compiler_options) & ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION)
1600+
&& array_is_const(Z_ARR(c->value))) {
1601+
return 1;
15691602
}
15701603
return 0;
15711604
}
@@ -1792,7 +1825,10 @@ static bool zend_try_ct_eval_class_const(zval *zv, zend_string *class_name, zend
17921825
c = &cc->value;
17931826

17941827
/* Substitute case-sensitive (or lowercase) persistent class constants */
1795-
if (Z_TYPE_P(c) < IS_OBJECT) {
1828+
if (Z_TYPE_P(c) < IS_ARRAY) {
1829+
ZVAL_COPY_OR_DUP(zv, c);
1830+
return 1;
1831+
} else if (Z_TYPE_P(c) == IS_ARRAY && array_is_const(Z_ARR_P(c))) {
17961832
ZVAL_COPY_OR_DUP(zv, c);
17971833
return 1;
17981834
}

ext/reflection/php_reflection.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,7 @@ static int format_default_value(smart_str *str, zval *value) {
643643
} ZEND_HASH_FOREACH_END();
644644
smart_str_appendc(str, ']');
645645
} else if (Z_TYPE_P(value) == IS_OBJECT) {
646+
/* This branch may only be reached for default properties, which don't support arbitrary objects. */
646647
zend_object *obj = Z_OBJ_P(value);
647648
zend_class_entry *class = obj->ce;
648649
ZEND_ASSERT(class->ce_flags & ZEND_ACC_ENUM);

ext/reflection/tests/gh11937_1.inc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
#[Attribute]
4+
class Attr {
5+
public function __construct(public $value) {}
6+
}
7+
8+
class Foo {
9+
public function __construct(public $value) {}
10+
}
11+
12+
#[Attr(new Foo(TestEnum::CASES))]
13+
function test() {}

ext/reflection/tests/gh11937_1.phpt

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
--TEST--
2+
GH-11937: Segfault in format_default_value due to unexpected enum/object
3+
--FILE--
4+
<?php
5+
6+
enum TestEnum {
7+
case One;
8+
case Two;
9+
const CASES = [self::One, self::Two];
10+
}
11+
12+
var_dump(TestEnum::CASES);
13+
14+
require __DIR__ . '/gh11937_1.inc';
15+
16+
echo (new ReflectionFunction('test'))->getAttributes('Attr')[0];
17+
18+
?>
19+
--EXPECT--
20+
array(2) {
21+
[0]=>
22+
enum(TestEnum::One)
23+
[1]=>
24+
enum(TestEnum::Two)
25+
}
26+
Attribute [ Attr ] {
27+
- Arguments [1] {
28+
Argument #0 [ new \Foo(TestEnum::CASES) ]
29+
}
30+
}

ext/reflection/tests/gh11937_2.inc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?php
2+
3+
#[Attr(FOOS)]
4+
function test() {}

ext/reflection/tests/gh11937_2.phpt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
--TEST--
2+
GH-11937: Segfault in format_default_value due to unexpected enum/object
3+
--FILE--
4+
<?php
5+
6+
class Foo {}
7+
8+
const FOOS = [new Foo()];
9+
10+
var_dump(FOOS);
11+
12+
require __DIR__ . '/gh11937_2.inc';
13+
14+
echo (new ReflectionFunction('test'))->getAttributes('Attr')[0];
15+
16+
?>
17+
--EXPECT--
18+
array(1) {
19+
[0]=>
20+
object(Foo)#1 (0) {
21+
}
22+
}
23+
Attribute [ Attr ] {
24+
- Arguments [1] {
25+
Argument #0 [ FOOS ]
26+
}
27+
}

0 commit comments

Comments
 (0)