Skip to content

Commit de26827

Browse files
committed
Real instance of lazy proxy may have less magic methods
In GH-18039 we guard the underlying property before forwarding access to the real instance of a lazy proxy. When the real instance lacks magic methods, the assertion zobj->ce->ce_flags & ZEND_ACC_USE_GUARDS fails in zend_get_property_guard(). Fix by checking that the real instance uses guards. Fixes GH-20504 Closes GH-21093
1 parent 6d6d013 commit de26827

File tree

7 files changed

+150
-8
lines changed

7 files changed

+150
-8
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ PHP NEWS
66
. Fixed bug GH-21029 (zend_mm_heap corrupted on Aarch64, LTO builds). (Arnaud)
77
. Fixed bug GH-20657 (Assertion failure in zend_lazy_object_get_info triggered
88
by setRawValueWithoutLazyInitialization() and newLazyGhost()). (Arnaud)
9+
. Fixed bug GH-20504 (Assertion failure in zend_get_property_guard when
10+
accessing properties on Reflection LazyProxy via isset()). (Arnaud)
911

1012
- Curl:
1113
. Fixed bug GH-21023 (CURLOPT_XFERINFOFUNCTION crash with a null callback).
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
--TEST--
2+
GH-20504: Assertion failure in zend_get_property_guard() when lazy proxy adds magic method - isset
3+
--CREDITS--
4+
vi3tL0u1s
5+
--FILE--
6+
<?php
7+
8+
class RealInstance {
9+
public $_;
10+
}
11+
class Proxy extends RealInstance {
12+
public function __isset($name) {
13+
return isset($this->$name['']);
14+
}
15+
}
16+
$rc = new ReflectionClass(Proxy::class);
17+
$obj = $rc->newLazyProxy(function () {
18+
return new RealInstance;
19+
});
20+
var_dump(isset($obj->name['']));
21+
22+
?>
23+
--EXPECT--
24+
bool(false)
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
--TEST--
2+
GH-20504: Assertion failure in zend_get_property_guard() when lazy proxy adds magic method - get
3+
--FILE--
4+
<?php
5+
6+
class RealInstance {
7+
public $_;
8+
}
9+
class Proxy extends RealInstance {
10+
public function __get($name) {
11+
return $this->$name;
12+
}
13+
}
14+
$rc = new ReflectionClass(Proxy::class);
15+
$obj = $rc->newLazyProxy(function () {
16+
return new RealInstance;
17+
});
18+
var_dump($obj->name);
19+
20+
?>
21+
--EXPECTF--
22+
Warning: Undefined property: RealInstance::$name in %s on line %d
23+
NULL
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
--TEST--
2+
GH-20504: Assertion failure in zend_get_property_guard() when lazy proxy adds magic method - set
3+
--FILE--
4+
<?php
5+
6+
#[AllowDynamicProperties]
7+
class RealInstance {
8+
public $_;
9+
}
10+
class Proxy extends RealInstance {
11+
public function __set($name, $value) {
12+
$this->$name = $value;
13+
}
14+
}
15+
$rc = new ReflectionClass(Proxy::class);
16+
$obj = $rc->newLazyProxy(function () {
17+
return new RealInstance;
18+
});
19+
$obj->name = 0;
20+
21+
var_dump($obj);
22+
23+
?>
24+
--EXPECTF--
25+
lazy proxy object(Proxy)#%d (1) {
26+
["instance"]=>
27+
object(RealInstance)#%d (2) {
28+
["_"]=>
29+
NULL
30+
["name"]=>
31+
int(0)
32+
}
33+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
--TEST--
2+
GH-20504: Assertion failure in zend_get_property_guard() when lazy proxy adds magic method - proxy defines __isset(), both have guards
3+
--FILE--
4+
<?php
5+
6+
class RealInstance {
7+
public $_;
8+
public function __get($name) {
9+
printf("%s::%s\n", static::class, __FUNCTION__);
10+
}
11+
}
12+
class Proxy extends RealInstance {
13+
public function __isset($name) {
14+
printf("%s::%s\n", static::class, __FUNCTION__);
15+
return isset($this->$name['']);
16+
}
17+
}
18+
$rc = new ReflectionClass(Proxy::class);
19+
$obj = $rc->newLazyProxy(function () {
20+
return new RealInstance;
21+
});
22+
var_dump(isset($obj->name['']));
23+
24+
?>
25+
--EXPECT--
26+
Proxy::__isset
27+
Proxy::__get
28+
bool(false)
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
--TEST--
2+
GH-20504: Assertion failure in zend_get_property_guard() when lazy proxy adds magic method - unset
3+
--FILE--
4+
<?php
5+
6+
class RealInstance {
7+
public $_;
8+
}
9+
class Proxy extends RealInstance {
10+
public function __unset($name) {
11+
unset($this->$name);
12+
}
13+
}
14+
$rc = new ReflectionClass(Proxy::class);
15+
$obj = $rc->newLazyProxy(function () {
16+
return new RealInstance;
17+
});
18+
unset($obj->name);
19+
20+
var_dump($obj);
21+
22+
?>
23+
--EXPECTF--
24+
lazy proxy object(Proxy)#%d (1) {
25+
["instance"]=>
26+
object(RealInstance)#%d (1) {
27+
["_"]=>
28+
NULL
29+
}
30+
}

Zend/zend_object_handlers.c

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -956,25 +956,27 @@ ZEND_API zval *zend_std_read_property(zend_object *zobj, zend_string *name, int
956956
uninit_error:
957957
if (UNEXPECTED(zend_lazy_object_must_init(zobj))) {
958958
if (!prop_info || (Z_PROP_FLAG_P(retval) & IS_PROP_LAZY)) {
959-
zobj = zend_lazy_object_init(zobj);
960-
if (!zobj) {
959+
zend_object *instance = zend_lazy_object_init(zobj);
960+
if (!instance) {
961961
retval = &EG(uninitialized_zval);
962962
goto exit;
963963
}
964964

965-
if (UNEXPECTED(guard)) {
965+
if (UNEXPECTED(guard && (instance->ce->ce_flags & ZEND_ACC_USE_GUARDS))) {
966+
/* Find which guard was used on zobj, so we can set the same
967+
* guard on instance. */
966968
uint32_t guard_type = (type == BP_VAR_IS) && zobj->ce->__isset
967969
? IN_ISSET : IN_GET;
968-
guard = zend_get_property_guard(zobj, name);
970+
guard = zend_get_property_guard(instance, name);
969971
if (!((*guard) & guard_type)) {
970972
(*guard) |= guard_type;
971-
retval = zend_std_read_property(zobj, name, type, cache_slot, rv);
973+
retval = zend_std_read_property(instance, name, type, cache_slot, rv);
972974
(*guard) &= ~guard_type;
973975
return retval;
974976
}
975977
}
976978

977-
return zend_std_read_property(zobj, name, type, cache_slot, rv);
979+
return zend_std_read_property(instance, name, type, cache_slot, rv);
978980
}
979981
}
980982
if (type != BP_VAR_IS) {
@@ -1013,7 +1015,7 @@ static zval *forward_write_to_lazy_object(zend_object *zobj,
10131015
return &EG(error_zval);
10141016
}
10151017

1016-
if (UNEXPECTED(guarded)) {
1018+
if (UNEXPECTED(guarded && (instance->ce->ce_flags & ZEND_ACC_USE_GUARDS))) {
10171019
uint32_t *guard = zend_get_property_guard(instance, name);
10181020
if (!((*guard) & IN_SET)) {
10191021
(*guard) |= IN_SET;
@@ -1597,7 +1599,7 @@ ZEND_API void zend_std_unset_property(zend_object *zobj, zend_string *name, void
15971599
return;
15981600
}
15991601

1600-
if (UNEXPECTED(guard)) {
1602+
if (UNEXPECTED(guard && zobj->ce->ce_flags & ZEND_ACC_USE_GUARDS)) {
16011603
guard = zend_get_property_guard(zobj, name);
16021604
if (!((*guard) & IN_UNSET)) {
16031605
(*guard) |= IN_UNSET;

0 commit comments

Comments
 (0)