Skip to content

Commit 9ec303e

Browse files
iliaalarnaud-lb
authored andcommitted
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 26f5009 (GH-18039). Also fixes GH-20873 and GH-20854. Closes GH-20875
1 parent 69b455c commit 9ec303e

File tree

6 files changed

+163
-12
lines changed

6 files changed

+163
-12
lines changed

NEWS

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ PHP NEWS
55
- Bz2:
66
. Fix truncation of total output size causing erroneous errors. (ndossche)
77

8+
- Core:
9+
. Fixed bugs GH-20875, GH-20873, GH-20854 (Propagate IN_GET guard in
10+
get_property_ptr_ptr for lazy proxies). (iliaal)
11+
812
- GD:
913
. Fixed bug GH-21431 (phpinfo() to display libJPEG 10.0 support).
1014
(David Carlier)
@@ -27,7 +31,7 @@ PHP NEWS
2731
- PGSQL:
2832
. Fixed preprocessor silently guarding PGSQL_SUPPRESS_TIMESTAMPS support
2933
due to a typo. (KentarouTakeda)
30-
34+
3135
- SNMP:
3236
. Fixed bug GH-21336 (SNMP::setSecurity() undefined behavior with
3337
NULL arguments). (David Carlier)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
--TEST--
2+
GH-20854 (Assertion in ZEND_RETURN_BY_REF with lazy proxy and return-by-ref __get)
3+
--FILE--
4+
<?php
5+
class C {
6+
public $prop;
7+
8+
function &__get($name) {
9+
return $this->x;
10+
}
11+
}
12+
13+
$rc = new ReflectionClass(C::class);
14+
$obj = $rc->newLazyProxy(function () {
15+
return new C;
16+
});
17+
$obj->x;
18+
echo "Done\n";
19+
?>
20+
--EXPECTF--
21+
Deprecated: Creation of dynamic property C::$x is deprecated in %s on line %d
22+
Done
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
--TEST--
2+
GH-20873 (Assertion failure in _zendi_try_convert_scalar_to_number with lazy proxy)
3+
--FILE--
4+
<?php
5+
class A {
6+
public $_;
7+
public function __get($n) {
8+
global $obj;
9+
$obj->x =& $this->_;
10+
static $a = $a;
11+
$e =& $this->_ - $a;
12+
}
13+
}
14+
$rc = new ReflectionClass(A::class);
15+
$obj = $rc->newLazyProxy(fn() => new A);
16+
$rc->initializeLazyObject($obj);
17+
var_dump($obj->p);
18+
?>
19+
--EXPECTF--
20+
Deprecated: Creation of dynamic property A::$x is deprecated in %s on line %d
21+
22+
Warning: Undefined variable $a in %s on line %d
23+
24+
Notice: Indirect modification of overloaded property A::$x has no effect in %s on line %d
25+
26+
Fatal error: Uncaught Error: Cannot assign by reference to overloaded object in %s:%d
27+
Stack trace:
28+
#0 %s(%d): A->__get('p')
29+
#1 {main}
30+
thrown in %s on line %d
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
--TEST--
2+
GH-20875 (Assertion failure in _get_zval_ptr_tmp with lazy proxy)
3+
--FILE--
4+
<?php
5+
class A {
6+
public $_;
7+
public function __get($name) {
8+
global $obj;
9+
$obj->f =& $this->b - $x > $y = new StdClass;
10+
static $a = $a;
11+
$t = 'x';
12+
foreach (get_defined_vars() as $key => $e) {}
13+
if ($v ==!1) $x = $a ?: $t = "ok";
14+
}
15+
}
16+
$rc = new ReflectionClass(A::class);
17+
$obj = $rc->newLazyProxy(function () { return new A; });
18+
$real = $rc->initializeLazyObject($obj);
19+
var_dump($real->prop);
20+
?>
21+
--EXPECTF--
22+
Deprecated: Creation of dynamic property A::$b is deprecated in %s on line %d
23+
24+
Deprecated: Creation of dynamic property A::$f is deprecated in %s on line %d
25+
26+
Warning: Undefined variable $x in %s on line %d
27+
28+
Notice: Object of class stdClass could not be converted to int in %s on line %d
29+
30+
Warning: Undefined variable $a in %s on line %d
31+
32+
Warning: Undefined variable $v in %s on line %d
33+
34+
Notice: Indirect modification of overloaded property A::$b has no effect in %s on line %d
35+
36+
Warning: Undefined variable $x in %s on line %d
37+
38+
Notice: Object of class stdClass could not be converted to int in %s on line %d
39+
40+
Warning: Undefined variable $v in %s on line %d
41+
42+
Notice: Indirect modification of overloaded property A::$f has no effect in %s on line %d
43+
44+
Fatal error: Uncaught Error: Cannot assign by reference to overloaded object in %s:%d
45+
Stack trace:
46+
#0 %s(%d): A->__get('b')
47+
#1 %s(%d): A->__get('prop')
48+
#2 {main}
49+
thrown in %s on line %d
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
--TEST--
2+
GH-20875 (Lazy proxy should not initialize when __get handles dynamic property access)
3+
--FILE--
4+
<?php
5+
6+
class Foo {
7+
public $_;
8+
9+
public function &__get($name) {
10+
echo "__get\n";
11+
return $name;
12+
}
13+
}
14+
15+
$proxy = (new ReflectionClass(Foo::class))->newLazyProxy(function () {
16+
echo "init\n";
17+
return new Foo();
18+
});
19+
$x = &$proxy->x;
20+
21+
?>
22+
--EXPECT--
23+
__get

Zend/zend_object_handlers.c

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1401,12 +1401,24 @@ ZEND_API zval *zend_std_get_property_ptr_ptr(zend_object *zobj, zend_string *nam
14011401
UNEXPECTED((*zend_get_property_guard(zobj, name)) & IN_GET) ||
14021402
UNEXPECTED(prop_info && (Z_PROP_FLAG_P(retval) & IS_PROP_UNINIT))) {
14031403
if (UNEXPECTED(zend_lazy_object_must_init(zobj) && (Z_PROP_FLAG_P(retval) & IS_PROP_LAZY))) {
1404-
zobj = zend_lazy_object_init(zobj);
1405-
if (!zobj) {
1404+
bool guarded = zobj->ce->__get
1405+
&& (*zend_get_property_guard(zobj, name) & IN_GET);
1406+
zend_object *instance = zend_lazy_object_init(zobj);
1407+
if (!instance) {
14061408
return &EG(error_zval);
14071409
}
14081410

1409-
return zend_std_get_property_ptr_ptr(zobj, name, type, cache_slot);
1411+
if (guarded && (instance->ce->ce_flags & ZEND_ACC_USE_GUARDS)) {
1412+
uint32_t *guard = zend_get_property_guard(instance, name);
1413+
if (!(*guard & IN_GET)) {
1414+
(*guard) |= IN_GET;
1415+
retval = zend_std_get_property_ptr_ptr(instance, name, type, cache_slot);
1416+
(*guard) &= ~IN_GET;
1417+
return retval;
1418+
}
1419+
}
1420+
1421+
return zend_std_get_property_ptr_ptr(instance, name, type, cache_slot);
14101422
}
14111423
if (UNEXPECTED(type == BP_VAR_RW || type == BP_VAR_R)) {
14121424
if (prop_info) {
@@ -1449,6 +1461,25 @@ ZEND_API zval *zend_std_get_property_ptr_ptr(zend_object *zobj, zend_string *nam
14491461
}
14501462
if (EXPECTED(!zobj->ce->__get) ||
14511463
UNEXPECTED((*zend_get_property_guard(zobj, name)) & IN_GET)) {
1464+
if (UNEXPECTED(zend_lazy_object_must_init(zobj))) {
1465+
bool guarded = (zobj->ce->__get != NULL);
1466+
zend_object *instance = zend_lazy_object_init(zobj);
1467+
if (!instance) {
1468+
return &EG(error_zval);
1469+
}
1470+
1471+
if (guarded && (instance->ce->ce_flags & ZEND_ACC_USE_GUARDS)) {
1472+
uint32_t *guard = zend_get_property_guard(instance, name);
1473+
if (!(*guard & IN_GET)) {
1474+
(*guard) |= IN_GET;
1475+
retval = zend_std_get_property_ptr_ptr(instance, name, type, cache_slot);
1476+
(*guard) &= ~IN_GET;
1477+
return retval;
1478+
}
1479+
}
1480+
1481+
return zend_std_get_property_ptr_ptr(instance, name, type, cache_slot);
1482+
}
14521483
if (UNEXPECTED(zobj->ce->ce_flags & ZEND_ACC_NO_DYNAMIC_PROPERTIES)) {
14531484
zend_forbidden_dynamic_property(zobj->ce, name);
14541485
return &EG(error_zval);
@@ -1458,14 +1489,6 @@ ZEND_API zval *zend_std_get_property_ptr_ptr(zend_object *zobj, zend_string *nam
14581489
return &EG(error_zval);
14591490
}
14601491
}
1461-
if (UNEXPECTED(zend_lazy_object_must_init(zobj))) {
1462-
zobj = zend_lazy_object_init(zobj);
1463-
if (!zobj) {
1464-
return &EG(error_zval);
1465-
}
1466-
1467-
return zend_std_get_property_ptr_ptr(zobj, name, type, cache_slot);
1468-
}
14691492
if (UNEXPECTED(!zobj->properties)) {
14701493
rebuild_object_properties_internal(zobj);
14711494
}

0 commit comments

Comments
 (0)