Skip to content

Commit 24b191a

Browse files
committed
Fix ReflectionProperty::getRawValue() and related methods for properties overridden with hooks
`new Reflectionproperty($scope, $propName)` keeps a reference to the zend_property_info of $propName declared in $scope. In getRawValue() and related methods, we use this reference to check whether the property is hooked. Calling `new ReflectionProperty($scope, $propName)->getRawValue($object)` is equivalent to the expression $object->$propName from scope $scope (except that it bypasses hooks), and thus may access an overridden property (unless the original is private). This property may have hooks and different flags. Here I fetch the effective property info before checking for hooks and property flags. Fixes GH-17713 Closes GH-17714
1 parent e13d25e commit 24b191a

File tree

4 files changed

+240
-42
lines changed

4 files changed

+240
-42
lines changed

NEWS

+2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ PHP NEWS
1515
by reference). (ilutov)
1616
. Fixed bug GH-17718 (Calling static methods on an interface that has
1717
`__callStatic` is allowed). (timwolla)
18+
. Fixed bug GH-17713 (ReflectionProperty::getRawValue() and related methods
19+
may call hooks of overridden properties). (Arnaud)
1820

1921
- DOM:
2022
. Fixed bug GH-17609 (Typo in error message: Dom\NO_DEFAULT_NS instead of

Zend/tests/lazy_objects/skipLazyInitialization.phpt

+8-8
Original file line numberDiff line numberDiff line change
@@ -198,10 +198,10 @@ getValue(): string(5) "value"
198198
## Property [ public static $static = 'static' ]
199199

200200
skipInitializerForProperty():
201-
ReflectionException: Can not use skipLazyInitialization on static property B::$static
201+
ReflectionException: Can not use skipLazyInitialization on static property A::$static
202202

203203
setRawValueWithoutLazyInitialization():
204-
ReflectionException: Can not use setRawValueWithoutLazyInitialization on static property B::$static
204+
ReflectionException: Can not use setRawValueWithoutLazyInitialization on static property A::$static
205205

206206
## Property [ public $noDefault = NULL ]
207207

@@ -238,10 +238,10 @@ getValue(): string(5) "value"
238238
## Property [ public $virtual ]
239239

240240
skipInitializerForProperty():
241-
ReflectionException: Can not use skipLazyInitialization on virtual property B::$virtual
241+
ReflectionException: Can not use skipLazyInitialization on virtual property A::$virtual
242242

243243
setRawValueWithoutLazyInitialization():
244-
ReflectionException: Can not use setRawValueWithoutLazyInitialization on virtual property B::$virtual
244+
ReflectionException: Can not use setRawValueWithoutLazyInitialization on virtual property A::$virtual
245245

246246
## Property [ $dynamicProp ]
247247

@@ -295,10 +295,10 @@ getValue(): string(5) "value"
295295
## Property [ public static $static = 'static' ]
296296

297297
skipInitializerForProperty():
298-
ReflectionException: Can not use skipLazyInitialization on static property B::$static
298+
ReflectionException: Can not use skipLazyInitialization on static property A::$static
299299

300300
setRawValueWithoutLazyInitialization():
301-
ReflectionException: Can not use setRawValueWithoutLazyInitialization on static property B::$static
301+
ReflectionException: Can not use setRawValueWithoutLazyInitialization on static property A::$static
302302

303303
## Property [ public $noDefault = NULL ]
304304

@@ -335,10 +335,10 @@ getValue(): string(5) "value"
335335
## Property [ public $virtual ]
336336

337337
skipInitializerForProperty():
338-
ReflectionException: Can not use skipLazyInitialization on virtual property B::$virtual
338+
ReflectionException: Can not use skipLazyInitialization on virtual property A::$virtual
339339

340340
setRawValueWithoutLazyInitialization():
341-
ReflectionException: Can not use setRawValueWithoutLazyInitialization on virtual property B::$virtual
341+
ReflectionException: Can not use setRawValueWithoutLazyInitialization on virtual property A::$virtual
342342

343343
## Property [ $dynamicProp ]
344344

ext/reflection/php_reflection.c

+61-34
Original file line numberDiff line numberDiff line change
@@ -6096,6 +6096,19 @@ ZEND_METHOD(ReflectionProperty, setValue)
60966096
}
60976097
/* }}} */
60986098

6099+
/* Return the property info being used when accessing 'ref->prop' from scope
6100+
* 'scope' on 'object'. The result may be different from 'ref->prop' when the
6101+
* property is overridden on 'object' and was not private in 'scope'.
6102+
* The effective prop may add hooks or change flags. */
6103+
static zend_property_info *reflection_property_get_effective_prop(
6104+
property_reference *ref, zend_class_entry *scope, zend_object *object) {
6105+
zend_property_info *prop = ref->prop;
6106+
if (scope != object->ce && !(prop && (prop->flags & ZEND_ACC_PRIVATE))) {
6107+
prop = zend_hash_find_ptr(&object->ce->properties_info, ref->unmangled_name);
6108+
}
6109+
return prop;
6110+
}
6111+
60996112
ZEND_METHOD(ReflectionProperty, getRawValue)
61006113
{
61016114
reflection_object *intern;
@@ -6108,17 +6121,20 @@ ZEND_METHOD(ReflectionProperty, getRawValue)
61086121

61096122
GET_REFLECTION_OBJECT_PTR(ref);
61106123

6111-
if (prop_get_flags(ref) & ZEND_ACC_STATIC) {
6112-
_DO_THROW("May not use getRawValue on static properties");
6124+
if (!instanceof_function(Z_OBJCE_P(object), intern->ce)) {
6125+
_DO_THROW("Given object is not an instance of the class this property was declared in");
61136126
RETURN_THROWS();
61146127
}
61156128

6116-
if (!instanceof_function(Z_OBJCE_P(object), intern->ce)) {
6117-
_DO_THROW("Given object is not an instance of the class this property was declared in");
6129+
zend_property_info *prop = reflection_property_get_effective_prop(ref,
6130+
intern->ce, Z_OBJ_P(object));
6131+
6132+
if (UNEXPECTED(prop && (prop->flags & ZEND_ACC_STATIC))) {
6133+
_DO_THROW("May not use getRawValue on static properties");
61186134
RETURN_THROWS();
61196135
}
61206136

6121-
if (!ref->prop || !ref->prop->hooks || !ref->prop->hooks[ZEND_PROPERTY_HOOK_GET]) {
6137+
if (!prop || !prop->hooks || !prop->hooks[ZEND_PROPERTY_HOOK_GET]) {
61226138
zval rv;
61236139
zval *member_p = zend_read_property_ex(intern->ce, Z_OBJ_P(object), ref->unmangled_name, 0, &rv);
61246140

@@ -6131,17 +6147,19 @@ ZEND_METHOD(ReflectionProperty, getRawValue)
61316147
RETURN_COPY_VALUE(member_p);
61326148
}
61336149
} else {
6134-
zend_function *func = zend_get_property_hook_trampoline(ref->prop, ZEND_PROPERTY_HOOK_GET, ref->unmangled_name);
6150+
zend_function *func = zend_get_property_hook_trampoline(prop, ZEND_PROPERTY_HOOK_GET, ref->unmangled_name);
61356151
zend_call_known_instance_method_with_0_params(func, Z_OBJ_P(object), return_value);
61366152
}
61376153
}
61386154

6139-
static void reflection_property_set_raw_value(property_reference *ref, reflection_object *intern, zend_object *object, zval *value)
6155+
static void reflection_property_set_raw_value(zend_property_info *prop,
6156+
zend_string *unmangled_name, reflection_object *intern,
6157+
zend_object *object, zval *value)
61406158
{
6141-
if (!ref->prop || !ref->prop->hooks || !ref->prop->hooks[ZEND_PROPERTY_HOOK_SET]) {
6142-
zend_update_property_ex(intern->ce, object, ref->unmangled_name, value);
6159+
if (!prop || !prop->hooks || !prop->hooks[ZEND_PROPERTY_HOOK_SET]) {
6160+
zend_update_property_ex(intern->ce, object, unmangled_name, value);
61436161
} else {
6144-
zend_function *func = zend_get_property_hook_trampoline(ref->prop, ZEND_PROPERTY_HOOK_SET, ref->unmangled_name);
6162+
zend_function *func = zend_get_property_hook_trampoline(prop, ZEND_PROPERTY_HOOK_SET, unmangled_name);
61456163
zend_call_known_instance_method_with_1_params(func, object, NULL, value);
61466164
}
61476165
}
@@ -6155,55 +6173,59 @@ ZEND_METHOD(ReflectionProperty, setRawValue)
61556173

61566174
GET_REFLECTION_OBJECT_PTR(ref);
61576175

6158-
if (prop_get_flags(ref) & ZEND_ACC_STATIC) {
6159-
_DO_THROW("May not use setRawValue on static properties");
6176+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "oz", &object, &value) == FAILURE) {
61606177
RETURN_THROWS();
61616178
}
61626179

6163-
if (zend_parse_parameters(ZEND_NUM_ARGS(), "oz", &object, &value) == FAILURE) {
6180+
zend_property_info *prop = reflection_property_get_effective_prop(ref,
6181+
intern->ce, Z_OBJ_P(object));
6182+
6183+
if (UNEXPECTED(prop && (prop->flags & ZEND_ACC_STATIC))) {
6184+
_DO_THROW("May not use setRawValue on static properties");
61646185
RETURN_THROWS();
61656186
}
61666187

6167-
reflection_property_set_raw_value(ref, intern, Z_OBJ_P(object), value);
6188+
reflection_property_set_raw_value(prop, ref->unmangled_name, intern, Z_OBJ_P(object), value);
61686189
}
61696190

6170-
static zend_result reflection_property_check_lazy_compatible(reflection_object *intern,
6171-
property_reference *ref, zend_object *object, const char *method)
6191+
static zend_result reflection_property_check_lazy_compatible(
6192+
zend_property_info *prop, zend_string *unmangled_name,
6193+
reflection_object *intern, zend_object *object, const char *method)
61726194
{
6173-
if (!ref->prop) {
6195+
if (!prop) {
61746196
zend_throw_exception_ex(reflection_exception_ptr, 0,
61756197
"Can not use %s on dynamic property %s::$%s",
61766198
method, ZSTR_VAL(intern->ce->name),
6177-
ZSTR_VAL(ref->unmangled_name));
6199+
ZSTR_VAL(unmangled_name));
61786200
return FAILURE;
61796201
}
61806202

6181-
if (ref->prop->flags & ZEND_ACC_STATIC) {
6203+
if (prop->flags & ZEND_ACC_STATIC) {
61826204
zend_throw_exception_ex(reflection_exception_ptr, 0,
61836205
"Can not use %s on static property %s::$%s",
6184-
method, ZSTR_VAL(intern->ce->name),
6185-
ZSTR_VAL(ref->unmangled_name));
6206+
method, ZSTR_VAL(prop->ce->name),
6207+
ZSTR_VAL(unmangled_name));
61866208
return FAILURE;
61876209
}
61886210

6189-
if (ref->prop->flags & ZEND_ACC_VIRTUAL) {
6211+
if (prop->flags & ZEND_ACC_VIRTUAL) {
61906212
zend_throw_exception_ex(reflection_exception_ptr, 0,
61916213
"Can not use %s on virtual property %s::$%s",
6192-
method, ZSTR_VAL(intern->ce->name),
6193-
ZSTR_VAL(ref->unmangled_name));
6214+
method, ZSTR_VAL(prop->ce->name),
6215+
ZSTR_VAL(unmangled_name));
61946216
return FAILURE;
61956217
}
61966218

61976219
if (UNEXPECTED(object->handlers->write_property != zend_std_write_property)) {
61986220
if (!zend_class_can_be_lazy(object->ce)) {
61996221
zend_throw_exception_ex(reflection_exception_ptr, 0,
62006222
"Can not use %s on internal class %s",
6201-
method, ZSTR_VAL(intern->ce->name));
6223+
method, ZSTR_VAL(object->ce->name));
62026224
return FAILURE;
62036225
}
62046226
}
62056227

6206-
ZEND_ASSERT(IS_VALID_PROPERTY_OFFSET(ref->prop->offset));
6228+
ZEND_ASSERT(IS_VALID_PROPERTY_OFFSET(prop->offset));
62076229

62086230
return SUCCESS;
62096231
}
@@ -6223,23 +6245,27 @@ ZEND_METHOD(ReflectionProperty, setRawValueWithoutLazyInitialization)
62236245
Z_PARAM_ZVAL(value)
62246246
} ZEND_PARSE_PARAMETERS_END();
62256247

6226-
if (reflection_property_check_lazy_compatible(intern, ref, object,
6227-
"setRawValueWithoutLazyInitialization") == FAILURE) {
6228-
RETURN_THROWS();
6229-
}
6230-
62316248
while (zend_object_is_lazy_proxy(object)
62326249
&& zend_lazy_object_initialized(object)) {
62336250
object = zend_lazy_object_get_instance(object);
62346251
}
62356252

6236-
zval *var_ptr = OBJ_PROP(object, ref->prop->offset);
6253+
zend_property_info *prop = reflection_property_get_effective_prop(ref,
6254+
intern->ce, object);
6255+
6256+
if (reflection_property_check_lazy_compatible(prop, ref->unmangled_name,
6257+
intern, object, "setRawValueWithoutLazyInitialization") == FAILURE) {
6258+
RETURN_THROWS();
6259+
}
6260+
6261+
zval *var_ptr = OBJ_PROP(object, prop->offset);
62376262
bool prop_was_lazy = Z_PROP_FLAG_P(var_ptr) & IS_PROP_LAZY;
62386263

62396264
/* Do not trigger initialization */
62406265
Z_PROP_FLAG_P(var_ptr) &= ~IS_PROP_LAZY;
62416266

6242-
reflection_property_set_raw_value(ref, intern, object, value);
6267+
reflection_property_set_raw_value(prop, ref->unmangled_name, intern, object,
6268+
value);
62436269

62446270
/* Mark property as lazy again if an exception prevented update */
62456271
if (EG(exception) && prop_was_lazy && Z_TYPE_P(var_ptr) == IS_UNDEF
@@ -6271,7 +6297,8 @@ ZEND_METHOD(ReflectionProperty, skipLazyInitialization)
62716297
Z_PARAM_OBJ_OF_CLASS(object, intern->ce)
62726298
} ZEND_PARSE_PARAMETERS_END();
62736299

6274-
if (reflection_property_check_lazy_compatible(intern, ref, object,
6300+
if (reflection_property_check_lazy_compatible(ref->prop,
6301+
ref->unmangled_name, intern, object,
62756302
"skipLazyInitialization") == FAILURE) {
62766303
RETURN_THROWS();
62776304
}

0 commit comments

Comments
 (0)