diff --git a/Zend/tests/gh21687.phpt b/Zend/tests/gh21687.phpt new file mode 100644 index 000000000000..602f406b6105 --- /dev/null +++ b/Zend/tests/gh21687.phpt @@ -0,0 +1,32 @@ +--TEST-- +GH-21687 (array_walk corrupts readonly and enum properties by wrapping them in IS_REFERENCE) +--FILE-- +getMessage() . "\n"; +} +var_dump($bar); + +class MyObj { + public function __construct(public readonly string $name = "test") {} +} +$obj = new MyObj(); +try { + array_walk($obj, function(&$v) { $v = "modified"; }); +} catch (\Error $e) { + echo $e->getMessage() . "\n"; +} +echo $obj->name . "\n"; +?> +--EXPECT-- +Cannot acquire reference to readonly property Foo::$name +enum(Foo::Bar) +Cannot acquire reference to readonly property MyObj::$name +test diff --git a/ext/standard/array.c b/ext/standard/array.c index 037f13a7a464..6432506b3ed1 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -1409,10 +1409,19 @@ static zend_result php_array_walk( /* Add type source for property references. */ if (Z_TYPE_P(zv) != IS_REFERENCE && Z_TYPE_P(array) == IS_OBJECT) { zend_property_info *prop_info = - zend_get_typed_property_info_for_slot(Z_OBJ_P(array), zv); + zend_get_property_info_for_slot(Z_OBJ_P(array), zv); if (prop_info) { - ZVAL_NEW_REF(zv, zv); - ZEND_REF_ADD_TYPE_SOURCE(Z_REF_P(zv), prop_info); + if (UNEXPECTED(prop_info->flags & ZEND_ACC_READONLY)) { + zend_throw_error(NULL, + "Cannot acquire reference to readonly property %s::$%s", + ZSTR_VAL(prop_info->ce->name), + zend_get_unmangled_property_name(prop_info->name)); + break; + } + if (ZEND_TYPE_IS_SET(prop_info->type)) { + ZVAL_NEW_REF(zv, zv); + ZEND_REF_ADD_TYPE_SOURCE(Z_REF_P(zv), prop_info); + } } } }