Skip to content

Commit c34eb6c

Browse files
committed
Check readonly when directly using cache_slot
1 parent c1b6b73 commit c34eb6c

File tree

4 files changed

+76
-11
lines changed

4 files changed

+76
-11
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
--TEST--
2+
Test interaction with cache slots
3+
--FILE--
4+
<?php
5+
6+
class Test {
7+
public readonly string $prop;
8+
public readonly array $prop2;
9+
public function setProp(string $prop) {
10+
$this->prop = $prop;
11+
}
12+
public function initAndAppendProp2() {
13+
$this->prop2 = [];
14+
$this->prop2[] = 1;
15+
}
16+
}
17+
18+
$test = new Test;
19+
$test->setProp("a");
20+
var_dump($test->prop);
21+
try {
22+
$test->setProp("b");
23+
} catch (Error $e) {
24+
echo $e->getMessage(), "\n";
25+
}
26+
var_dump($test->prop);
27+
echo "\n";
28+
29+
$test = new Test;
30+
try {
31+
$test->initAndAppendProp2();
32+
} catch (Error $e) {
33+
echo $e->getMessage(), "\n";
34+
}
35+
try {
36+
$test->initAndAppendProp2();
37+
} catch (Error $e) {
38+
echo $e->getMessage(), "\n";
39+
}
40+
var_dump($test->prop2);
41+
42+
?>
43+
--EXPECT--
44+
string(1) "a"
45+
Cannot modify readonly property Test::$prop
46+
string(1) "a"
47+
48+
Cannot modify readonly property Test::$prop2
49+
Cannot modify readonly property Test::$prop2
50+
array(0) {
51+
}

Zend/zend_execute.c

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -823,6 +823,12 @@ ZEND_COLD zend_never_inline void zend_verify_property_type_error(zend_property_i
823823
zend_string_release(type_str);
824824
}
825825

826+
ZEND_COLD zend_never_inline void zend_readonly_property_modification_error(
827+
zend_property_info *info) {
828+
zend_throw_error(NULL, "Cannot modify readonly property %s::$%s",
829+
ZSTR_VAL(info->ce->name), zend_get_unmangled_property_name(info->name));
830+
}
831+
826832
static zend_class_entry *resolve_single_class_type(zend_string *name, zend_class_entry *self_ce) {
827833
if (zend_string_equals_literal_ci(name, "self")) {
828834
/* We need to explicitly check for this here, to avoid updating the type in the trait and
@@ -927,6 +933,11 @@ static zend_never_inline zval* zend_assign_to_typed_prop(zend_property_info *inf
927933
{
928934
zval tmp;
929935

936+
if (UNEXPECTED(info->flags & ZEND_ACC_READONLY)) {
937+
zend_readonly_property_modification_error(info);
938+
return &EG(uninitialized_zval);
939+
}
940+
930941
ZVAL_DEREF(value);
931942
ZVAL_COPY(&tmp, value);
932943

@@ -2819,9 +2830,16 @@ static zend_always_inline void zend_fetch_property_address(zval *result, zval *c
28192830
ptr = OBJ_PROP(zobj, prop_offset);
28202831
if (EXPECTED(Z_TYPE_P(ptr) != IS_UNDEF)) {
28212832
ZVAL_INDIRECT(result, ptr);
2822-
if (flags) {
2823-
zend_property_info *prop_info = CACHED_PTR_EX(cache_slot + 2);
2824-
if (prop_info) {
2833+
zend_property_info *prop_info = CACHED_PTR_EX(cache_slot + 2);
2834+
if (prop_info) {
2835+
if (UNEXPECTED(prop_info->flags & ZEND_ACC_READONLY)
2836+
&& Z_TYPE_P(ptr) != IS_OBJECT) {
2837+
ZEND_ASSERT(type == BP_VAR_W || type == BP_VAR_RW || type == BP_VAR_UNSET);
2838+
zend_readonly_property_modification_error(prop_info);
2839+
ZVAL_ERROR(result);
2840+
return;
2841+
}
2842+
if (flags) {
28252843
zend_handle_fetch_obj_flags(result, ptr, NULL, prop_info, flags);
28262844
}
28272845
}

Zend/zend_execute.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ ZEND_API ZEND_COLD void zend_throw_ref_type_error_type(zend_property_info *prop1
6868
ZEND_API ZEND_COLD zval* ZEND_FASTCALL zend_undefined_offset_write(HashTable *ht, zend_long lval);
6969
ZEND_API ZEND_COLD zval* ZEND_FASTCALL zend_undefined_index_write(HashTable *ht, zend_string *offset);
7070

71+
ZEND_COLD zend_never_inline void zend_readonly_property_modification_error(zend_property_info *info);
72+
7173
ZEND_API bool zend_verify_scalar_type_hint(uint32_t type_mask, zval *arg, bool strict, bool is_internal_arg);
7274
ZEND_API ZEND_COLD void zend_verify_arg_error(
7375
const zend_function *zf, const zend_arg_info *arg_info, uint32_t arg_num, zval *value);

Zend/zend_object_handlers.c

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -277,12 +277,6 @@ static ZEND_COLD zend_never_inline void zend_forbidden_dynamic_property(
277277
ZSTR_VAL(ce->name), ZSTR_VAL(member));
278278
}
279279

280-
static ZEND_COLD zend_never_inline void zend_readonly_property_modification_error(
281-
zend_class_entry *ce, zend_string *member) {
282-
zend_throw_error(NULL, "Cannot modify readonly property %s::$%s",
283-
ZSTR_VAL(ce->name), ZSTR_VAL(member));
284-
}
285-
286280
static ZEND_COLD zend_never_inline void zend_readonly_property_modification_scope_error(
287281
zend_class_entry *ce, zend_string *member, zend_class_entry *scope, const char *operation) {
288282
zend_throw_error(NULL, "Cannot %s readonly property %s::$%s from %s%s",
@@ -595,7 +589,7 @@ ZEND_API zval *zend_std_read_property(zend_object *zobj, zend_string *name, int
595589
if (prop_info && UNEXPECTED(prop_info->flags & ZEND_ACC_READONLY)
596590
&& (type == BP_VAR_W || type == BP_VAR_RW || type == BP_VAR_UNSET)) {
597591
if (Z_TYPE_P(retval) != IS_OBJECT) {
598-
zend_readonly_property_modification_error(prop_info->ce, name);
592+
zend_readonly_property_modification_error(prop_info);
599593
retval = &EG(uninitialized_zval);
600594
}
601595
}
@@ -781,7 +775,7 @@ ZEND_API zval *zend_std_write_property(zend_object *zobj, zend_string *name, zva
781775
if (UNEXPECTED(prop_info)) {
782776
if (UNEXPECTED(prop_info->flags & ZEND_ACC_READONLY)) {
783777
Z_TRY_DELREF_P(value);
784-
zend_readonly_property_modification_error(prop_info->ce, name);
778+
zend_readonly_property_modification_error(prop_info);
785779
variable_ptr = &EG(error_zval);
786780
goto exit;
787781
}

0 commit comments

Comments
 (0)