Fix segfault when evaluating const expr default value of child prop with added hooks

Introduced by GH-17870. Not adding a NEWS entry since this is fixed in
the same version.

Fixes oss-fuzz #403816122
Closes GH-18098
This commit is contained in:
Ilija Tovilo 2025-03-17 16:25:42 +01:00
parent 7d1a2d03e4
commit d5bdf8f508
No known key found for this signature in database
GPG key ID: 5050C66BFCD1015A
4 changed files with 63 additions and 21 deletions

View file

@ -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"

View file

@ -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;

View file

@ -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_object_dtor_property(obj, p);
ZVAL_COPY_VALUE_PROP(p, &properties_table_snapshot[i]);
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 (!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[OBJ_PROP_TO_NUM(prop_info->offset)]);
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;
}
}

View file

@ -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;
}