Skip to content

Commit 71a2c3f

Browse files
-
1 parent 1e00cef commit 71a2c3f

File tree

8 files changed

+74
-24
lines changed

8 files changed

+74
-24
lines changed

Zend/zend_API.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1744,7 +1744,7 @@ ZEND_API void object_properties_load(zend_object *object, const HashTable *prope
17441744
zval *slot = OBJ_PROP(object, property_info->offset);
17451745
if (UNEXPECTED((property_info->flags & ZEND_ACC_READONLY) && !Z_ISUNDEF_P(slot))) {
17461746
if (Z_PROP_FLAG_P(slot) & IS_PROP_REINITABLE) {
1747-
Z_PROP_FLAG_P(slot) &= ~IS_PROP_REINITABLE;
1747+
Z_PROP_FLAG_P(slot) &= ~(IS_PROP_REINITABLE | IS_PROP_CTOR_REINITABLE);
17481748
} else {
17491749
zend_readonly_property_modification_error(property_info);
17501750
return;

Zend/zend_execute.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1074,7 +1074,7 @@ static zend_never_inline zval* zend_assign_to_typed_prop(const zend_property_inf
10741074

10751075
if (UNEXPECTED(info->flags & (ZEND_ACC_READONLY|ZEND_ACC_PPP_SET_MASK))) {
10761076
if ((info->flags & ZEND_ACC_READONLY)
1077-
&& (!zend_readonly_property_is_reinitable(property_val)
1077+
&& (!zend_readonly_property_is_reinitable_for_context(property_val, info)
10781078
|| zend_is_foreign_cpp_overwrite(property_val, info))) {
10791079
zend_readonly_property_modification_error(info);
10801080
return &EG(uninitialized_zval);
@@ -1093,7 +1093,7 @@ static zend_never_inline zval* zend_assign_to_typed_prop(const zend_property_inf
10931093
return &EG(uninitialized_zval);
10941094
}
10951095

1096-
Z_PROP_FLAG_P(property_val) &= ~IS_PROP_REINITABLE;
1096+
Z_PROP_FLAG_P(property_val) &= ~(IS_PROP_REINITABLE | IS_PROP_CTOR_REINITABLE);
10971097

10981098
return zend_assign_to_variable_ex(property_val, &tmp, IS_TMP_VAR, EX_USES_STRICT_TYPES(), garbage_ptr);
10991099
}
@@ -5930,14 +5930,14 @@ static zend_never_inline void zend_ctor_clear_promoted_readonly_reinitable_slow(
59305930
&& zend_has_more_derived_promoted_override(obj->ce, ctor_scope, prop_info->name)) {
59315931
continue;
59325932
}
5933-
Z_PROP_FLAG_P(OBJ_PROP(obj, prop_info->offset)) &= ~IS_PROP_REINITABLE;
5933+
Z_PROP_FLAG_P(OBJ_PROP(obj, prop_info->offset)) &= ~(IS_PROP_REINITABLE | IS_PROP_CTOR_REINITABLE);
59345934
}
59355935
} ZEND_HASH_FOREACH_END();
59365936
}
59375937

59385938
/* Clear IS_PROP_REINITABLE from all promoted readonly properties of the exiting
59395939
* constructor's scope. Called for both 'new Foo()' and 'parent::__construct()'. */
5940-
static zend_always_inline void zend_ctor_clear_promoted_readonly_reinitable(zend_execute_data *ex, uint32_t call_info)
5940+
ZEND_API void ZEND_FASTCALL zend_ctor_clear_promoted_readonly_reinitable(zend_execute_data *ex, uint32_t call_info)
59415941
{
59425942
if ((call_info & ZEND_CALL_HAS_THIS)
59435943
&& (ex->func->common.fn_flags & ZEND_ACC_CTOR)

Zend/zend_execute.h

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -623,6 +623,48 @@ static zend_always_inline bool zend_has_active_derived_ctor_with_promoted_proper
623623
return false;
624624
}
625625

626+
static zend_always_inline bool zend_has_active_ctor_with_promoted_property(
627+
const zend_execute_data *ex, zend_object *obj, zend_string *property_name)
628+
{
629+
for (const zend_execute_data *frame = ex; frame != NULL; frame = frame->prev_execute_data) {
630+
if (!(ZEND_CALL_INFO(frame) & ZEND_CALL_HAS_THIS)
631+
|| !(frame->func->common.fn_flags & ZEND_ACC_CTOR)
632+
|| Z_OBJ(frame->This) != obj) {
633+
continue;
634+
}
635+
636+
zend_class_entry *scope = frame->func->common.scope;
637+
if (scope == NULL) {
638+
continue;
639+
}
640+
641+
zend_property_info *scope_prop = (zend_property_info *) zend_hash_find_ptr(
642+
&scope->properties_info, property_name);
643+
if (scope_prop != NULL
644+
&& (scope_prop->flags & (ZEND_ACC_READONLY | ZEND_ACC_PROMOTED))
645+
== (ZEND_ACC_READONLY | ZEND_ACC_PROMOTED)) {
646+
return true;
647+
}
648+
}
649+
650+
return false;
651+
}
652+
653+
static zend_always_inline bool zend_readonly_property_is_reinitable_for_context(
654+
const zval *property_val, const zend_property_info *prop_info)
655+
{
656+
if (!(Z_PROP_FLAG_P(property_val) & IS_PROP_REINITABLE)) {
657+
return false;
658+
}
659+
if (!(Z_PROP_FLAG_P(property_val) & IS_PROP_CTOR_REINITABLE)) {
660+
return true;
661+
}
662+
zend_execute_data *ex = EG(current_execute_data);
663+
return ex
664+
&& (ZEND_CALL_INFO(ex) & ZEND_CALL_HAS_THIS)
665+
&& zend_has_active_ctor_with_promoted_property(ex, Z_OBJ(ex->This), prop_info->name);
666+
}
667+
626668
/* Check if a foreign constructor is attempting a CPP initial assignment on an
627669
* already-initialized property owned by a different class (e.g., child has CPP
628670
* for $x, parent's CPP also tries to set $x on the child's object). */
@@ -655,6 +697,7 @@ ZEND_COLD void zend_verify_class_constant_type_error(const zend_class_constant *
655697
ZEND_API bool zend_verify_property_type(const zend_property_info *info, zval *property, bool strict);
656698
ZEND_COLD void zend_verify_property_type_error(const zend_property_info *info, const zval *property);
657699
ZEND_COLD void zend_magic_get_property_type_inconsistency_error(const zend_property_info *info, const zval *property);
700+
ZEND_API void ZEND_FASTCALL zend_ctor_clear_promoted_readonly_reinitable(zend_execute_data *ex, uint32_t call_info);
658701

659702
#define ZEND_REF_ADD_TYPE_SOURCE(ref, source) \
660703
zend_ref_add_type_source(&ZEND_REF_TYPE_SOURCES(ref), source)

Zend/zend_object_handlers.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1068,7 +1068,7 @@ ZEND_API zval *zend_std_write_property(zend_object *zobj, zend_string *name, zva
10681068
if (error) {
10691069
if ((prop_info->flags & ZEND_ACC_READONLY)
10701070
&& Z_TYPE_P(variable_ptr) != IS_UNDEF
1071-
&& (!zend_readonly_property_is_reinitable(variable_ptr)
1071+
&& (!zend_readonly_property_is_reinitable_for_context(variable_ptr, prop_info)
10721072
|| zend_is_foreign_cpp_overwrite(variable_ptr, prop_info))) {
10731073
zend_readonly_property_modification_error(prop_info);
10741074
variable_ptr = &EG(error_zval);
@@ -1135,9 +1135,9 @@ ZEND_API zval *zend_std_write_property(zend_object *zobj, zend_string *name, zva
11351135
}
11361136
}
11371137
if (reinitable) {
1138-
Z_PROP_FLAG_P(variable_ptr) = IS_PROP_REINITABLE;
1138+
Z_PROP_FLAG_P(variable_ptr) = IS_PROP_REINITABLE | IS_PROP_CTOR_REINITABLE;
11391139
} else {
1140-
Z_PROP_FLAG_P(variable_ptr) &= ~(IS_PROP_UNINIT|IS_PROP_REINITABLE);
1140+
Z_PROP_FLAG_P(variable_ptr) &= ~(IS_PROP_UNINIT|IS_PROP_REINITABLE|IS_PROP_CTOR_REINITABLE);
11411141
}
11421142
value = &tmp;
11431143
}
@@ -1556,7 +1556,7 @@ ZEND_API void zend_std_unset_property(zend_object *zobj, zend_string *name, void
15561556
if (error) {
15571557
if ((prop_info->flags & ZEND_ACC_READONLY)
15581558
&& Z_TYPE_P(slot) != IS_UNDEF
1559-
&& !(Z_PROP_FLAG_P(slot) & IS_PROP_REINITABLE)) {
1559+
&& !zend_readonly_property_is_reinitable_for_context(slot, prop_info)) {
15601560
zend_readonly_property_unset_error(prop_info->ce, name);
15611561
return;
15621562
}

Zend/zend_objects.c

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,8 @@ ZEND_API void ZEND_FASTCALL zend_objects_clone_members(zend_object *new_object,
208208
ZVAL_COPY_VALUE_PROP(dst, src);
209209
zval_add_ref(dst);
210210
if (has_clone_method) {
211-
/* Unconditionally add the IS_PROP_REINITABLE flag to avoid a potential cache miss of property_info */
211+
/* Clone opens its own reinitability window; stale ctor-only state must not leak into it. */
212+
Z_PROP_FLAG_P(dst) &= ~IS_PROP_CTOR_REINITABLE;
212213
Z_PROP_FLAG_P(dst) |= IS_PROP_REINITABLE;
213214
}
214215

@@ -257,7 +258,8 @@ ZEND_API void ZEND_FASTCALL zend_objects_clone_members(zend_object *new_object,
257258
zval_add_ref(&new_prop);
258259
}
259260
if (has_clone_method) {
260-
/* Unconditionally add the IS_PROP_REINITABLE flag to avoid a potential cache miss of property_info */
261+
/* Clone opens its own reinitability window; stale ctor-only state must not leak into it. */
262+
Z_PROP_FLAG_P(&new_prop) &= ~IS_PROP_CTOR_REINITABLE;
261263
Z_PROP_FLAG_P(&new_prop) |= IS_PROP_REINITABLE;
262264
}
263265
if (EXPECTED(key)) {
@@ -274,8 +276,8 @@ ZEND_API void ZEND_FASTCALL zend_objects_clone_members(zend_object *new_object,
274276
if (ZEND_CLASS_HAS_READONLY_PROPS(new_object->ce)) {
275277
for (uint32_t i = 0; i < new_object->ce->default_properties_count; i++) {
276278
zval* prop = OBJ_PROP_NUM(new_object, i);
277-
/* Unconditionally remove the IS_PROP_REINITABLE flag to avoid a potential cache miss of property_info */
278-
Z_PROP_FLAG_P(prop) &= ~IS_PROP_REINITABLE;
279+
/* Close both clone-opened and any stale ctor-opened reinitability state. */
280+
Z_PROP_FLAG_P(prop) &= ~(IS_PROP_REINITABLE | IS_PROP_CTOR_REINITABLE);
279281
}
280282
}
281283
}
@@ -290,6 +292,7 @@ ZEND_API zend_object *zend_objects_clone_obj_with(zend_object *old_object, const
290292
if (ZEND_CLASS_HAS_READONLY_PROPS(new_object->ce)) {
291293
for (uint32_t i = 0; i < new_object->ce->default_properties_count; i++) {
292294
zval* prop = OBJ_PROP_NUM(new_object, i);
295+
Z_PROP_FLAG_P(prop) &= ~IS_PROP_CTOR_REINITABLE;
293296
Z_PROP_FLAG_P(prop) |= IS_PROP_REINITABLE;
294297
}
295298
}

Zend/zend_types.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1594,6 +1594,7 @@ static zend_always_inline uint32_t zval_delref_p(zval* pz) {
15941594
#define IS_PROP_UNINIT (1<<0)
15951595
#define IS_PROP_REINITABLE (1<<1) /* It has impact only on readonly properties */
15961596
#define IS_PROP_LAZY (1<<2)
1597+
#define IS_PROP_CTOR_REINITABLE (1<<3)
15971598
#define Z_PROP_FLAG_P(z) Z_EXTRA_P(z)
15981599
#define ZVAL_COPY_VALUE_PROP(z, v) \
15991600
do { *(z) = *(v); } while (0)

ext/opcache/jit/zend_jit_helpers.c

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2807,7 +2807,7 @@ static zend_always_inline bool verify_readonly_and_avis(zval *property_val, zend
28072807
{
28082808
if (UNEXPECTED(info->flags & (ZEND_ACC_READONLY|ZEND_ACC_PPP_SET_MASK))) {
28092809
if ((info->flags & ZEND_ACC_READONLY)
2810-
&& (!zend_readonly_property_is_reinitable(property_val)
2810+
&& (!zend_readonly_property_is_reinitable_for_context(property_val, info)
28112811
|| zend_is_foreign_cpp_overwrite(property_val, info))) {
28122812
zend_readonly_property_modification_error(info);
28132813
return false;
@@ -2852,7 +2852,7 @@ static void ZEND_FASTCALL zend_jit_assign_to_typed_prop(zval *property_val, zend
28522852
return;
28532853
}
28542854

2855-
Z_PROP_FLAG_P(property_val) &= ~IS_PROP_REINITABLE;
2855+
Z_PROP_FLAG_P(property_val) &= ~(IS_PROP_REINITABLE | IS_PROP_CTOR_REINITABLE);
28562856

28572857
value = zend_assign_to_variable_ex(property_val, &tmp, IS_TMP_VAR, EX_USES_STRICT_TYPES(), &garbage);
28582858
if (result) {
@@ -2909,7 +2909,7 @@ static void ZEND_FASTCALL zend_jit_assign_op_to_typed_prop(zval *zptr, zend_prop
29092909

29102910
binary_op(&z_copy, zptr, value);
29112911
if (EXPECTED(zend_verify_property_type(prop_info, &z_copy, EX_USES_STRICT_TYPES()))) {
2912-
Z_PROP_FLAG_P(zptr) &= ~IS_PROP_REINITABLE;
2912+
Z_PROP_FLAG_P(zptr) &= ~(IS_PROP_REINITABLE | IS_PROP_CTOR_REINITABLE);
29132913
zval_ptr_dtor(zptr);
29142914
ZVAL_COPY_VALUE(zptr, &z_copy);
29152915
} else {
@@ -3006,13 +3006,13 @@ static void ZEND_FASTCALL zend_jit_inc_typed_prop(zval *var_ptr, zend_property_i
30063006
zend_long val = _zend_jit_throw_inc_prop_error(prop_info);
30073007
ZVAL_LONG(var_ptr, val);
30083008
} else {
3009-
Z_PROP_FLAG_P(var_ptr) &= ~IS_PROP_REINITABLE;
3009+
Z_PROP_FLAG_P(var_ptr) &= ~(IS_PROP_REINITABLE | IS_PROP_CTOR_REINITABLE);
30103010
}
30113011
} else if (UNEXPECTED(!zend_verify_property_type(prop_info, var_ptr, EX_USES_STRICT_TYPES()))) {
30123012
zval_ptr_dtor(var_ptr);
30133013
ZVAL_COPY_VALUE(var_ptr, &tmp);
30143014
} else {
3015-
Z_PROP_FLAG_P(var_ptr) &= ~IS_PROP_REINITABLE;
3015+
Z_PROP_FLAG_P(var_ptr) &= ~(IS_PROP_REINITABLE | IS_PROP_CTOR_REINITABLE);
30163016
zval_ptr_dtor(&tmp);
30173017
}
30183018
}
@@ -3038,13 +3038,13 @@ static void ZEND_FASTCALL zend_jit_dec_typed_prop(zval *var_ptr, zend_property_i
30383038
zend_long val = _zend_jit_throw_dec_prop_error(prop_info);
30393039
ZVAL_LONG(var_ptr, val);
30403040
} else {
3041-
Z_PROP_FLAG_P(var_ptr) &= ~IS_PROP_REINITABLE;
3041+
Z_PROP_FLAG_P(var_ptr) &= ~(IS_PROP_REINITABLE | IS_PROP_CTOR_REINITABLE);
30423042
}
30433043
} else if (UNEXPECTED(!zend_verify_property_type(prop_info, var_ptr, EX_USES_STRICT_TYPES()))) {
30443044
zval_ptr_dtor(var_ptr);
30453045
ZVAL_COPY_VALUE(var_ptr, &tmp);
30463046
} else {
3047-
Z_PROP_FLAG_P(var_ptr) &= ~IS_PROP_REINITABLE;
3047+
Z_PROP_FLAG_P(var_ptr) &= ~(IS_PROP_REINITABLE | IS_PROP_CTOR_REINITABLE);
30483048
zval_ptr_dtor(&tmp);
30493049
}
30503050
}
@@ -3086,14 +3086,14 @@ static void ZEND_FASTCALL zend_jit_post_inc_typed_prop(zval *var_ptr, zend_prope
30863086
zend_long val = _zend_jit_throw_inc_prop_error(prop_info);
30873087
ZVAL_LONG(var_ptr, val);
30883088
} else {
3089-
Z_PROP_FLAG_P(var_ptr) &= ~IS_PROP_REINITABLE;
3089+
Z_PROP_FLAG_P(var_ptr) &= ~(IS_PROP_REINITABLE | IS_PROP_CTOR_REINITABLE);
30903090
}
30913091
} else if (UNEXPECTED(!zend_verify_property_type(prop_info, var_ptr, EX_USES_STRICT_TYPES()))) {
30923092
zval_ptr_dtor(var_ptr);
30933093
ZVAL_COPY_VALUE(var_ptr, result);
30943094
ZVAL_UNDEF(result);
30953095
} else {
3096-
Z_PROP_FLAG_P(var_ptr) &= ~IS_PROP_REINITABLE;
3096+
Z_PROP_FLAG_P(var_ptr) &= ~(IS_PROP_REINITABLE | IS_PROP_CTOR_REINITABLE);
30973097
}
30983098
}
30993099

@@ -3120,14 +3120,14 @@ static void ZEND_FASTCALL zend_jit_post_dec_typed_prop(zval *var_ptr, zend_prope
31203120
zend_long val = _zend_jit_throw_dec_prop_error(prop_info);
31213121
ZVAL_LONG(var_ptr, val);
31223122
} else {
3123-
Z_PROP_FLAG_P(var_ptr) &= ~IS_PROP_REINITABLE;
3123+
Z_PROP_FLAG_P(var_ptr) &= ~(IS_PROP_REINITABLE | IS_PROP_CTOR_REINITABLE);
31243124
}
31253125
} else if (UNEXPECTED(!zend_verify_property_type(prop_info, var_ptr, EX_USES_STRICT_TYPES()))) {
31263126
zval_ptr_dtor(var_ptr);
31273127
ZVAL_COPY_VALUE(var_ptr, result);
31283128
ZVAL_UNDEF(result);
31293129
} else {
3130-
Z_PROP_FLAG_P(var_ptr) &= ~IS_PROP_REINITABLE;
3130+
Z_PROP_FLAG_P(var_ptr) &= ~(IS_PROP_REINITABLE | IS_PROP_CTOR_REINITABLE);
31313131
}
31323132
}
31333133

ext/opcache/jit/zend_jit_vm_helpers.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV zend_jit_leave_func_helper_tai
5858
}
5959

6060
zend_vm_stack_free_extra_args_ex(call_info, execute_data);
61+
zend_ctor_clear_promoted_readonly_reinitable(execute_data, call_info);
6162
if (UNEXPECTED(call_info & ZEND_CALL_RELEASE_THIS)) {
6263
OBJ_RELEASE(Z_OBJ(execute_data->This));
6364
} else if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) {
@@ -109,6 +110,7 @@ ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_leave_nested_func_helper(ZEND_OPC
109110
}
110111

111112
zend_vm_stack_free_extra_args_ex(call_info, execute_data);
113+
zend_ctor_clear_promoted_readonly_reinitable(execute_data, call_info);
112114
if (UNEXPECTED(call_info & ZEND_CALL_RELEASE_THIS)) {
113115
OBJ_RELEASE(Z_OBJ(execute_data->This));
114116
} else if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) {
@@ -151,6 +153,7 @@ ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_leave_top_func_helper(ZEND_OPCODE
151153
}
152154
zend_vm_stack_free_extra_args_ex(call_info, execute_data);
153155
}
156+
zend_ctor_clear_promoted_readonly_reinitable(execute_data, call_info);
154157
if (UNEXPECTED(call_info & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) {
155158
zend_free_extra_named_params(EX(extra_named_params));
156159
}

0 commit comments

Comments
 (0)