1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 00:02:20 +01:00

Fix GH-20875: Propagate IN_GET guard in get_property_ptr_ptr for lazy proxies

zend_std_get_property_ptr_ptr() was the only property handler that did
not propagate the IN_GET guard to the underlying object when forwarding
from a lazy proxy after initialization. This caused __get to be called
on the underlying object when it shouldn't be, leading to assertion
failures.

The same guard-copying pattern already existed in read_property,
write_property, unset_property, and has_property since commit
26f5009e91 (GH-18039).

Also fixes GH-20873 and GH-20854.

Closes GH-20875
This commit is contained in:
Ilia Alshanetsky
2026-03-16 20:20:52 -04:00
committed by Arnaud Le Blanc
parent 69b455c89e
commit 9ec303edde
6 changed files with 163 additions and 12 deletions

6
NEWS
View File

@@ -5,6 +5,10 @@ PHP NEWS
- Bz2:
. Fix truncation of total output size causing erroneous errors. (ndossche)
- Core:
. Fixed bugs GH-20875, GH-20873, GH-20854 (Propagate IN_GET guard in
get_property_ptr_ptr for lazy proxies). (iliaal)
- GD:
. Fixed bug GH-21431 (phpinfo() to display libJPEG 10.0 support).
(David Carlier)
@@ -27,7 +31,7 @@ PHP NEWS
- PGSQL:
. Fixed preprocessor silently guarding PGSQL_SUPPRESS_TIMESTAMPS support
due to a typo. (KentarouTakeda)
- SNMP:
. Fixed bug GH-21336 (SNMP::setSecurity() undefined behavior with
NULL arguments). (David Carlier)

View File

@@ -0,0 +1,22 @@
--TEST--
GH-20854 (Assertion in ZEND_RETURN_BY_REF with lazy proxy and return-by-ref __get)
--FILE--
<?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;
echo "Done\n";
?>
--EXPECTF--
Deprecated: Creation of dynamic property C::$x is deprecated in %s on line %d
Done

View File

@@ -0,0 +1,30 @@
--TEST--
GH-20873 (Assertion failure in _zendi_try_convert_scalar_to_number with lazy proxy)
--FILE--
<?php
class A {
public $_;
public function __get($n) {
global $obj;
$obj->x =& $this->_;
static $a = $a;
$e =& $this->_ - $a;
}
}
$rc = new ReflectionClass(A::class);
$obj = $rc->newLazyProxy(fn() => new A);
$rc->initializeLazyObject($obj);
var_dump($obj->p);
?>
--EXPECTF--
Deprecated: Creation of dynamic property A::$x is deprecated in %s on line %d
Warning: Undefined variable $a in %s on line %d
Notice: Indirect modification of overloaded property A::$x has no effect in %s on line %d
Fatal error: Uncaught Error: Cannot assign by reference to overloaded object in %s:%d
Stack trace:
#0 %s(%d): A->__get('p')
#1 {main}
thrown in %s on line %d

View File

@@ -0,0 +1,49 @@
--TEST--
GH-20875 (Assertion failure in _get_zval_ptr_tmp with lazy proxy)
--FILE--
<?php
class A {
public $_;
public function __get($name) {
global $obj;
$obj->f =& $this->b - $x > $y = new StdClass;
static $a = $a;
$t = 'x';
foreach (get_defined_vars() as $key => $e) {}
if ($v ==!1) $x = $a ?: $t = "ok";
}
}
$rc = new ReflectionClass(A::class);
$obj = $rc->newLazyProxy(function () { return new A; });
$real = $rc->initializeLazyObject($obj);
var_dump($real->prop);
?>
--EXPECTF--
Deprecated: Creation of dynamic property A::$b is deprecated in %s on line %d
Deprecated: Creation of dynamic property A::$f is deprecated in %s on line %d
Warning: Undefined variable $x in %s on line %d
Notice: Object of class stdClass could not be converted to int in %s on line %d
Warning: Undefined variable $a in %s on line %d
Warning: Undefined variable $v in %s on line %d
Notice: Indirect modification of overloaded property A::$b has no effect in %s on line %d
Warning: Undefined variable $x in %s on line %d
Notice: Object of class stdClass could not be converted to int in %s on line %d
Warning: Undefined variable $v in %s on line %d
Notice: Indirect modification of overloaded property A::$f has no effect in %s on line %d
Fatal error: Uncaught Error: Cannot assign by reference to overloaded object in %s:%d
Stack trace:
#0 %s(%d): A->__get('b')
#1 %s(%d): A->__get('prop')
#2 {main}
thrown in %s on line %d

View File

@@ -0,0 +1,23 @@
--TEST--
GH-20875 (Lazy proxy should not initialize when __get handles dynamic property access)
--FILE--
<?php
class Foo {
public $_;
public function &__get($name) {
echo "__get\n";
return $name;
}
}
$proxy = (new ReflectionClass(Foo::class))->newLazyProxy(function () {
echo "init\n";
return new Foo();
});
$x = &$proxy->x;
?>
--EXPECT--
__get

View File

@@ -1401,12 +1401,24 @@ try_again:
UNEXPECTED((*zend_get_property_guard(zobj, name)) & IN_GET) ||
UNEXPECTED(prop_info && (Z_PROP_FLAG_P(retval) & IS_PROP_UNINIT))) {
if (UNEXPECTED(zend_lazy_object_must_init(zobj) && (Z_PROP_FLAG_P(retval) & IS_PROP_LAZY))) {
zobj = zend_lazy_object_init(zobj);
if (!zobj) {
bool guarded = zobj->ce->__get
&& (*zend_get_property_guard(zobj, name) & IN_GET);
zend_object *instance = zend_lazy_object_init(zobj);
if (!instance) {
return &EG(error_zval);
}
return zend_std_get_property_ptr_ptr(zobj, name, type, cache_slot);
if (guarded && (instance->ce->ce_flags & ZEND_ACC_USE_GUARDS)) {
uint32_t *guard = zend_get_property_guard(instance, name);
if (!(*guard & IN_GET)) {
(*guard) |= IN_GET;
retval = zend_std_get_property_ptr_ptr(instance, name, type, cache_slot);
(*guard) &= ~IN_GET;
return retval;
}
}
return zend_std_get_property_ptr_ptr(instance, name, type, cache_slot);
}
if (UNEXPECTED(type == BP_VAR_RW || type == BP_VAR_R)) {
if (prop_info) {
@@ -1449,6 +1461,25 @@ try_again:
}
if (EXPECTED(!zobj->ce->__get) ||
UNEXPECTED((*zend_get_property_guard(zobj, name)) & IN_GET)) {
if (UNEXPECTED(zend_lazy_object_must_init(zobj))) {
bool guarded = (zobj->ce->__get != NULL);
zend_object *instance = zend_lazy_object_init(zobj);
if (!instance) {
return &EG(error_zval);
}
if (guarded && (instance->ce->ce_flags & ZEND_ACC_USE_GUARDS)) {
uint32_t *guard = zend_get_property_guard(instance, name);
if (!(*guard & IN_GET)) {
(*guard) |= IN_GET;
retval = zend_std_get_property_ptr_ptr(instance, name, type, cache_slot);
(*guard) &= ~IN_GET;
return retval;
}
}
return zend_std_get_property_ptr_ptr(instance, name, type, cache_slot);
}
if (UNEXPECTED(zobj->ce->ce_flags & ZEND_ACC_NO_DYNAMIC_PROPERTIES)) {
zend_forbidden_dynamic_property(zobj->ce, name);
return &EG(error_zval);
@@ -1458,14 +1489,6 @@ try_again:
return &EG(error_zval);
}
}
if (UNEXPECTED(zend_lazy_object_must_init(zobj))) {
zobj = zend_lazy_object_init(zobj);
if (!zobj) {
return &EG(error_zval);
}
return zend_std_get_property_ptr_ptr(zobj, name, type, cache_slot);
}
if (UNEXPECTED(!zobj->properties)) {
rebuild_object_properties_internal(zobj);
}