Skip to content

Commit 0f1d26f

Browse files
committed
Do not allow sideeffects when readonly property modification fails
1 parent ccc16b4 commit 0f1d26f

File tree

3 files changed

+52
-15
lines changed

3 files changed

+52
-15
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
--TEST--
2+
Test that there can be no side-effect when a readonly property modification fails
3+
--FILE--
4+
<?php
5+
6+
class Foo {
7+
public function __construct(
8+
public readonly string $bar
9+
) {}
10+
11+
public function write()
12+
{
13+
$this->bar = new S();
14+
}
15+
}
16+
17+
class S {
18+
public function __toString() {
19+
var_dump("Side-effect in __toString()");
20+
return "";
21+
}
22+
}
23+
24+
$foo = new Foo("");
25+
26+
try {
27+
$foo->write();
28+
} catch (Error $e) {
29+
echo $e->getMessage() . "\n";
30+
}
31+
32+
?>
33+
--EXPECT--
34+
Cannot modify readonly property Foo::$bar

Zend/zend_execute.c

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1009,6 +1009,13 @@ static zend_never_inline zval* zend_assign_to_typed_prop(zend_property_info *inf
10091009
{
10101010
zval tmp;
10111011

1012+
if (UNEXPECTED(
1013+
(info->flags & ZEND_ACC_READONLY) && !(Z_PROP_FLAG_P(property_val) & IS_PROP_REINITABLE)
1014+
)) {
1015+
zend_readonly_property_modification_error(info);
1016+
return &EG(uninitialized_zval);
1017+
}
1018+
10121019
ZVAL_DEREF(value);
10131020
ZVAL_COPY(&tmp, value);
10141021

@@ -1018,13 +1025,7 @@ static zend_never_inline zval* zend_assign_to_typed_prop(zend_property_info *inf
10181025
}
10191026

10201027
if (UNEXPECTED(info->flags & ZEND_ACC_READONLY)) {
1021-
if (Z_PROP_FLAG_P(property_val) & IS_PROP_REINITABLE) {
1022-
Z_PROP_FLAG_P(property_val) &= ~IS_PROP_REINITABLE;
1023-
} else {
1024-
zval_ptr_dtor(&tmp);
1025-
zend_readonly_property_modification_error(info);
1026-
return &EG(uninitialized_zval);
1027-
}
1028+
Z_PROP_FLAG_P(property_val) &= ~IS_PROP_REINITABLE;
10281029
}
10291030

10301031
return zend_assign_to_variable(property_val, &tmp, IS_TMP_VAR, EX_USES_STRICT_TYPES());

Zend/zend_object_handlers.c

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -812,6 +812,15 @@ ZEND_API zval *zend_std_write_property(zend_object *zobj, zend_string *name, zva
812812
Z_TRY_ADDREF_P(value);
813813

814814
if (UNEXPECTED(prop_info)) {
815+
if (UNEXPECTED(
816+
(prop_info->flags & ZEND_ACC_READONLY) && !(Z_PROP_FLAG_P(variable_ptr) & IS_PROP_REINITABLE)
817+
)) {
818+
Z_TRY_DELREF_P(value);
819+
zend_readonly_property_modification_error(prop_info);
820+
variable_ptr = &EG(error_zval);
821+
goto exit;
822+
}
823+
815824
ZVAL_COPY_VALUE(&tmp, value);
816825
// Increase refcount to prevent object from being released in __toString()
817826
GC_ADDREF(zobj);
@@ -829,14 +838,7 @@ ZEND_API zval *zend_std_write_property(zend_object *zobj, zend_string *name, zva
829838
goto exit;
830839
}
831840
if (UNEXPECTED(prop_info->flags & ZEND_ACC_READONLY)) {
832-
if (Z_PROP_FLAG_P(variable_ptr) & IS_PROP_REINITABLE) {
833-
Z_PROP_FLAG_P(variable_ptr) &= ~IS_PROP_REINITABLE;
834-
} else {
835-
zval_ptr_dtor(&tmp);
836-
zend_readonly_property_modification_error(prop_info);
837-
variable_ptr = &EG(error_zval);
838-
goto exit;
839-
}
841+
Z_PROP_FLAG_P(variable_ptr) &= ~IS_PROP_REINITABLE;
840842
}
841843
value = &tmp;
842844
}

0 commit comments

Comments
 (0)