diff --git a/Zend/tests/oss-fuzz-403816122.phpt b/Zend/tests/oss-fuzz-403816122.phpt new file mode 100644 index 000000000000..b9d9400609fc --- /dev/null +++ b/Zend/tests/oss-fuzz-403816122.phpt @@ -0,0 +1,22 @@ +--TEST-- +OSS-Fuzz #403816122: Segfault when initializing default properties of child prop with added hooks +--FILE-- +<?php + +const X = 'x'; + +class P { + public $prop; +} + +class C extends P { + public $prop = X { + get => 'y'; + } +} + +var_dump((new C)->prop); + +?> +--EXPECT-- +string(1) "y" diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 1021a6f7bd50..5aac3c1f7d77 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -1613,8 +1613,12 @@ ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type) / /* Use the default properties table to also update initializers of private properties * that have been shadowed in a child class. */ for (uint32_t i = 0; i < class_type->default_properties_count; i++) { - val = &default_properties_table[i]; prop_info = class_type->properties_info_table[i]; + if (!prop_info) { + continue; + } + + val = &default_properties_table[OBJ_PROP_TO_NUM(prop_info->offset)]; if (Z_TYPE_P(val) == IS_CONSTANT_AST && UNEXPECTED(update_property(val, prop_info) != SUCCESS)) { return FAILURE; diff --git a/Zend/zend_lazy_objects.c b/Zend/zend_lazy_objects.c index 41ba3e1cd288..d1b950160e1c 100644 --- a/Zend/zend_lazy_objects.c +++ b/Zend/zend_lazy_objects.c @@ -268,14 +268,17 @@ ZEND_API zend_object *zend_object_make_lazy(zend_object *obj, obj = zend_objects_new(reflection_ce); - for (int i = 0; i < obj->ce->default_properties_count; i++) { + /* Iterate in reverse to avoid overriding Z_PROP_FLAG_P() of child props with added hooks (GH-17870). */ + for (int i = obj->ce->default_properties_count - 1; i >= 0; i--) { zval *p = &obj->properties_table[i]; ZVAL_UNDEF(p); - if (EXPECTED(obj->ce->properties_info_table[i])) { + Z_PROP_FLAG_P(p) = 0; + + zend_property_info *prop_info = obj->ce->properties_info_table[i]; + if (prop_info) { + zval *p = &obj->properties_table[OBJ_PROP_TO_NUM(prop_info->offset)]; Z_PROP_FLAG_P(p) = IS_PROP_UNINIT | IS_PROP_LAZY; lazy_properties_count++; - } else { - Z_PROP_FLAG_P(p) = 0; } } } else { @@ -326,7 +329,7 @@ ZEND_API zend_object *zend_object_make_lazy(zend_object *obj, for (int i = 0; i < reflection_ce->default_properties_count; i++) { zend_property_info *prop_info = obj->ce->properties_info_table[i]; if (EXPECTED(prop_info)) { - zval *p = &obj->properties_table[i]; + zval *p = &obj->properties_table[OBJ_PROP_TO_NUM(prop_info->offset)]; if (Z_TYPE_P(p) != IS_UNDEF) { if ((prop_info->flags & ZEND_ACC_READONLY) && !(Z_PROP_FLAG_P(p) & IS_PROP_REINITABLE) /* TODO: test final property */ @@ -408,12 +411,16 @@ static void zend_lazy_object_revert_init(zend_object *obj, zval *properties_tabl zval *properties_table = obj->properties_table; for (int i = 0; i < ce->default_properties_count; i++) { - zval *p = &properties_table[i]; + zend_property_info *prop_info = ce->properties_info_table[i]; + if (!prop_info) { + continue; + } + + zval *p = &properties_table[OBJ_PROP_TO_NUM(prop_info->offset)]; zend_object_dtor_property(obj, p); - ZVAL_COPY_VALUE_PROP(p, &properties_table_snapshot[i]); + ZVAL_COPY_VALUE_PROP(p, &properties_table_snapshot[OBJ_PROP_TO_NUM(prop_info->offset)]); - zend_property_info *prop_info = ce->properties_info_table[i]; - if (Z_ISREF_P(p) && prop_info && ZEND_TYPE_IS_SET(prop_info->type)) { + if (Z_ISREF_P(p) && ZEND_TYPE_IS_SET(prop_info->type)) { ZEND_REF_ADD_TYPE_SOURCE(Z_REF_P(p), prop_info); } } @@ -526,10 +533,12 @@ static zend_object *zend_lazy_object_init_proxy(zend_object *obj) obj->properties = NULL; for (int i = 0; i < Z_OBJ(retval)->ce->default_properties_count; i++) { - if (EXPECTED(Z_OBJ(retval)->ce->properties_info_table[i])) { - zend_object_dtor_property(obj, &obj->properties_table[i]); - ZVAL_UNDEF(&obj->properties_table[i]); - Z_PROP_FLAG_P(&obj->properties_table[i]) = IS_PROP_UNINIT | IS_PROP_LAZY; + zend_property_info *prop_info = Z_OBJ(retval)->ce->properties_info_table[i]; + if (EXPECTED(prop_info)) { + zval *prop = &obj->properties_table[OBJ_PROP_TO_NUM(prop_info->offset)]; + zend_object_dtor_property(obj, prop); + ZVAL_UNDEF(prop); + Z_PROP_FLAG_P(prop) = IS_PROP_UNINIT | IS_PROP_LAZY; } } @@ -722,13 +731,16 @@ zend_object *zend_lazy_object_clone(zend_object *old_obj) zend_class_entry *ce = old_obj->ce; zend_object *new_proxy = zend_objects_new(ce); - for (int i = 0; i < ce->default_properties_count; i++) { + /* Iterate in reverse to avoid overriding Z_PROP_FLAG_P() of child props with added hooks (GH-17870). */ + for (int i = ce->default_properties_count - 1; i >= 0; i--) { zval *p = &new_proxy->properties_table[i]; ZVAL_UNDEF(p); - if (EXPECTED(ce->properties_info_table[i])) { + Z_PROP_FLAG_P(p) = 0; + + zend_property_info *prop_info = ce->properties_info_table[i]; + if (prop_info) { + zval *p = &new_proxy->properties_table[OBJ_PROP_TO_NUM(prop_info->offset)]; Z_PROP_FLAG_P(p) = IS_PROP_UNINIT | IS_PROP_LAZY; - } else { - Z_PROP_FLAG_P(p) = 0; } } diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 6e53b987d676..59ed26339e9c 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -3815,9 +3815,13 @@ static bool preload_try_resolve_constants(zend_class_entry *ce) bool resolved = true; for (i = 0; i < ce->default_properties_count; i++) { - val = &ce->default_properties_table[i]; + zend_property_info *prop = ce->properties_info_table[i]; + if (!prop) { + continue; + } + + val = &ce->default_properties_table[OBJ_PROP_TO_NUM(prop->offset)]; if (Z_TYPE_P(val) == IS_CONSTANT_AST) { - zend_property_info *prop = ce->properties_info_table[i]; if (UNEXPECTED(zval_update_constant_ex(val, prop->ce) != SUCCESS)) { resolved = ok = false; }