-
Notifications
You must be signed in to change notification settings - Fork 8k
Description
Description
The following code:
<?php
class C {
public $prop;
function &__get($name) {
return $this->x;
}
}
$rc = new ReflectionClass(C::class);
$obj = $rc->newLazyProxy(function () {
return new C;
});
$obj->x;Resulted in this output:
Deprecated: Creation of dynamic property C::$x is deprecated in oss-fuzz-471993725.php on line 7
Warning: Undefined property: C::$x in Zend/tests/oss-fuzz-471993725.php on line 7
php: Zend/zend_vm_execute.h:23271: ZEND_RETURN_BY_REF_SPEC_VAR_HANDLER: Assertion `retval_ptr != &(executor_globals.uninitialized_zval)' failed.
Termsig=6
- The first
FETCH_OBJ_Rwill call__get,xis guarded on the proxy. - The access in
__getcompiles toFETCH_OBJ_W, which will callget_property_ptr_ptr()on the proxy. xis guarded, so the property access is attempted.- Because the proxy is lazy, the object is initialized,
zobjis replaced. get_property_ptr_ptr()is repeated, but this time on the underlying object.- This time it fails, because
xis unguarded on the underlying, so the code assumesread_property()will be called. - However, we're inside the nested
get_property_ptr_ptr()call here, and will just propagateNULL. - We'll now return to the outer
FETCH_OBJ_Wcall on the proxyget_property_ptr_ptr()has returnedNULL, andread_property()&EG(uninitialized_zval), even though the offset is guarded, which normally doesn't happen.
I don't know exactly what the best approach is to fix this. Intuitively I'd expect the guards for both the proxy and underlying object to be shared, which should make this problem go away. But that's not completely straight-forward to implement, because the lazy object might use guards before the underlying object even exists, and the underlying object doesn't have a fast, direct reference to the proxy (only through EG(lazy_objects_store). This would be ok if we knew the object is an underlying object of a proxy, but there's no such flag either, and performing the lookup on any object is obviously bad.
The other approach would be to copy the guard to the underlying object before calling get_property_ptr_ptr() on it again. There are multiple places where this would need to be adjusted, and this still wouldn't solve the expectation when handling the underlying object directly elsewhere.
/cc @arnaud-lb I hope my description was understandable. Do you have any suggestions?
PHP Version
PHP 8.4+
Operating System
No response