Skip to content

Commit 9437aa9

Browse files
committed
Merge branch 'PHP-8.1' into PHP-8.2
* PHP-8.1: Fix segfault in format_default_value due to unexpected enum/object
2 parents 06bda99 + f78d1d0 commit 9437aa9

File tree

7 files changed

+116
-2
lines changed

7 files changed

+116
-2
lines changed

NEWS

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

5+
- Core:
6+
. Fixed bug GH-11937 (Constant ASTs containing objects). (ilutov)
7+
58
- MySQLnd:
69
. Fixed bug GH-10270 (Invalid error message when connection via SSL fails:
710
"trying to connect via (null)"). (Kamil Tekiela)

Zend/zend_compile.c

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1423,6 +1423,35 @@ ZEND_API zend_result zend_unmangle_property_name_ex(const zend_string *name, con
14231423
}
14241424
/* }}} */
14251425

1426+
static bool array_is_const_ex(zend_array *array, uint32_t *max_checks)
1427+
{
1428+
if (zend_hash_num_elements(array) > *max_checks) {
1429+
return false;
1430+
}
1431+
*max_checks -= zend_hash_num_elements(array);
1432+
1433+
zval *element;
1434+
ZEND_HASH_FOREACH_VAL(array, element) {
1435+
if (Z_TYPE_P(element) < IS_ARRAY) {
1436+
continue;
1437+
} else if (Z_TYPE_P(element) == IS_ARRAY) {
1438+
if (!array_is_const_ex(array, max_checks)) {
1439+
return false;
1440+
}
1441+
} else if (UNEXPECTED(Z_TYPE_P(element) >=IS_OBJECT)) {
1442+
return false;
1443+
}
1444+
} ZEND_HASH_FOREACH_END();
1445+
1446+
return true;
1447+
}
1448+
1449+
static bool array_is_const(zend_array *array)
1450+
{
1451+
uint32_t max_checks = 50;
1452+
return array_is_const_ex(array, &max_checks);
1453+
}
1454+
14261455
static bool can_ct_eval_const(zend_constant *c) {
14271456
if (ZEND_CONSTANT_FLAGS(c) & CONST_DEPRECATED) {
14281457
return 0;
@@ -1433,9 +1462,13 @@ static bool can_ct_eval_const(zend_constant *c) {
14331462
&& (CG(compiler_options) & ZEND_COMPILE_WITH_FILE_CACHE))) {
14341463
return 1;
14351464
}
1436-
if (Z_TYPE(c->value) < IS_OBJECT
1465+
if (Z_TYPE(c->value) < IS_ARRAY
14371466
&& !(CG(compiler_options) & ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION)) {
14381467
return 1;
1468+
} else if (Z_TYPE(c->value) == IS_ARRAY
1469+
&& !(CG(compiler_options) & ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION)
1470+
&& array_is_const(Z_ARR(c->value))) {
1471+
return 1;
14391472
}
14401473
return 0;
14411474
}
@@ -1660,7 +1693,10 @@ static bool zend_try_ct_eval_class_const(zval *zv, zend_string *class_name, zend
16601693
c = &cc->value;
16611694

16621695
/* Substitute case-sensitive (or lowercase) persistent class constants */
1663-
if (Z_TYPE_P(c) < IS_OBJECT) {
1696+
if (Z_TYPE_P(c) < IS_ARRAY) {
1697+
ZVAL_COPY_OR_DUP(zv, c);
1698+
return 1;
1699+
} else if (Z_TYPE_P(c) == IS_ARRAY && array_is_const(Z_ARR_P(c))) {
16641700
ZVAL_COPY_OR_DUP(zv, c);
16651701
return 1;
16661702
}

ext/reflection/php_reflection.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -639,6 +639,7 @@ static int format_default_value(smart_str *str, zval *value) {
639639
} ZEND_HASH_FOREACH_END();
640640
smart_str_appendc(str, ']');
641641
} else if (Z_TYPE_P(value) == IS_OBJECT) {
642+
/* This branch may only be reached for default properties, which don't support arbitrary objects. */
642643
zend_object *obj = Z_OBJ_P(value);
643644
zend_class_entry *class = obj->ce;
644645
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)