From 572753faafeec1ff034bf5d493475624cfefb1a7 Mon Sep 17 00:00:00 2001 From: zaaarf Date: Tue, 12 May 2026 15:24:24 +0200 Subject: [PATCH 1/6] ext/reflection: ensure __isset is not returning a reference before calling it --- ext/reflection/php_reflection.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 94d8bb7d149c..ff93235e198a 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -6724,6 +6724,11 @@ ZEND_METHOD(ReflectionProperty, isReadable) handle_magic_get: if (ce->__get) { if (obj && ce->__isset) { + if (ce->__isset->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) { + zend_throw_exception(reflection_exception_ptr, "__isset should not return a reference", 0); + RETURN_THROWS(); + } + uint32_t *guard = zend_get_property_guard(obj, ref->unmangled_name); if (!((*guard) & ZEND_GUARD_PROPERTY_ISSET)) { GC_ADDREF(obj); From 132446f1d80242a54b54b48d5aa84987df72e88b Mon Sep 17 00:00:00 2001 From: zaaarf Date: Tue, 12 May 2026 16:45:25 +0200 Subject: [PATCH 2/6] GH-22000 - add test --- ext/reflection/tests/gh22000.phpt | 39 +++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 ext/reflection/tests/gh22000.phpt diff --git a/ext/reflection/tests/gh22000.phpt b/ext/reflection/tests/gh22000.phpt new file mode 100644 index 000000000000..87be92f87077 --- /dev/null +++ b/ext/reflection/tests/gh22000.phpt @@ -0,0 +1,39 @@ +--TEST-- +GH-22000 - Ensure __isset is not returning a reference in ReflectionProperty::isReadable() +--FILE-- +b); + unset($this->c); + } + + public function &__isset($name) { + return $name === 'c'; + } + + public function __get($name) {} +} + + +function test($class) { + try { + $rc = new ReflectionClass($class); + foreach ($rc->getProperties() as $rp) { + echo $rp->getName() . ' from global: '; + var_dump($rp->isReadable(null, new $class)); + } + } catch(\Exception $ex) { + echo $ex->getMessage(); + } +} + +test('TestClass'); +?> +--EXPECTF-- +a from global: bool(false) +b from global: __isset should not return a reference From 277f0f4cd46eef8c77e5d5446c2a9ad14e907a07 Mon Sep 17 00:00:00 2001 From: zaaarf Date: Wed, 13 May 2026 01:18:12 +0200 Subject: [PATCH 3/6] ext/reflection: only warn instead of throwing exceptions on bad __isset --- ext/reflection/php_reflection.c | 5 +++-- ext/reflection/tests/gh22000.phpt | 22 ++++++++++++---------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index ff93235e198a..22c0e5ec3060 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -6725,8 +6725,9 @@ ZEND_METHOD(ReflectionProperty, isReadable) if (ce->__get) { if (obj && ce->__isset) { if (ce->__isset->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) { - zend_throw_exception(reflection_exception_ptr, "__isset should not return a reference", 0); - RETURN_THROWS(); + //php_error_docref(NULL, E_WARNING, "__isset unexpectedly returned a reference!"); + zend_error(E_WARNING, "__isset unexpectedly returned a reference!"); + RETURN_FALSE; } uint32_t *guard = zend_get_property_guard(obj, ref->unmangled_name); diff --git a/ext/reflection/tests/gh22000.phpt b/ext/reflection/tests/gh22000.phpt index 87be92f87077..fc757113150e 100644 --- a/ext/reflection/tests/gh22000.phpt +++ b/ext/reflection/tests/gh22000.phpt @@ -21,19 +21,21 @@ class TestClass { function test($class) { - try { - $rc = new ReflectionClass($class); - foreach ($rc->getProperties() as $rp) { - echo $rp->getName() . ' from global: '; - var_dump($rp->isReadable(null, new $class)); - } - } catch(\Exception $ex) { - echo $ex->getMessage(); + $rc = new ReflectionClass($class); + foreach ($rc->getProperties() as $rp) { + echo $rp->getName() . ' from global:'; + var_dump($rp->isReadable(null, new $class)); } } test('TestClass'); ?> --EXPECTF-- -a from global: bool(false) -b from global: __isset should not return a reference +a from global:bool(false) +b from global: +Warning: __isset unexpectedly returned a reference! in %s/ext/reflection/tests/gh22000.php on line %d +bool(false) +c from global: +Warning: __isset unexpectedly returned a reference! in %s/ext/reflection/tests/gh22000.php on line %d +bool(false) + From 88a296b23a17850f9bd14e8fe3b68873d6d01f84 Mon Sep 17 00:00:00 2001 From: zaaarf Date: Wed, 13 May 2026 02:22:25 +0200 Subject: [PATCH 4/6] fix test separators --- ext/reflection/tests/gh22000.phpt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/reflection/tests/gh22000.phpt b/ext/reflection/tests/gh22000.phpt index fc757113150e..92a9629e64b2 100644 --- a/ext/reflection/tests/gh22000.phpt +++ b/ext/reflection/tests/gh22000.phpt @@ -33,9 +33,9 @@ test('TestClass'); --EXPECTF-- a from global:bool(false) b from global: -Warning: __isset unexpectedly returned a reference! in %s/ext/reflection/tests/gh22000.php on line %d +Warning: __isset unexpectedly returned a reference! in %s%eext%ereflection%etests%egh22000.php on line %d bool(false) c from global: -Warning: __isset unexpectedly returned a reference! in %s/ext/reflection/tests/gh22000.php on line %d +Warning: __isset unexpectedly returned a reference! in %s%eext%ereflection%etests%egh22000.php on line %d bool(false) From 666e493af8520072517092f2d780c2c5fda53b29 Mon Sep 17 00:00:00 2001 From: zaaarf Date: Wed, 13 May 2026 16:05:21 +0200 Subject: [PATCH 5/6] just unwrap --- ext/reflection/php_reflection.c | 7 +------ ext/reflection/tests/gh22000.phpt | 7 +++---- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 22c0e5ec3060..3244a39450f1 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -6724,12 +6724,6 @@ ZEND_METHOD(ReflectionProperty, isReadable) handle_magic_get: if (ce->__get) { if (obj && ce->__isset) { - if (ce->__isset->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) { - //php_error_docref(NULL, E_WARNING, "__isset unexpectedly returned a reference!"); - zend_error(E_WARNING, "__isset unexpectedly returned a reference!"); - RETURN_FALSE; - } - uint32_t *guard = zend_get_property_guard(obj, ref->unmangled_name); if (!((*guard) & ZEND_GUARD_PROPERTY_ISSET)) { GC_ADDREF(obj); @@ -6737,6 +6731,7 @@ ZEND_METHOD(ReflectionProperty, isReadable) zval member; ZVAL_STR(&member, ref->unmangled_name); zend_call_known_instance_method_with_1_params(ce->__isset, obj, return_value, &member); + zend_unwrap_reference(return_value); *guard &= ~ZEND_GUARD_PROPERTY_ISSET; OBJ_RELEASE(obj); return; diff --git a/ext/reflection/tests/gh22000.phpt b/ext/reflection/tests/gh22000.phpt index 92a9629e64b2..a742030792e8 100644 --- a/ext/reflection/tests/gh22000.phpt +++ b/ext/reflection/tests/gh22000.phpt @@ -33,9 +33,8 @@ test('TestClass'); --EXPECTF-- a from global:bool(false) b from global: -Warning: __isset unexpectedly returned a reference! in %s%eext%ereflection%etests%egh22000.php on line %d +Notice: Only variable references should be returned by reference in /home/zaaarf/dev/irl/c/php/ext/reflection/tests/gh22000.php on line 13 bool(false) c from global: -Warning: __isset unexpectedly returned a reference! in %s%eext%ereflection%etests%egh22000.php on line %d -bool(false) - +Notice: Only variable references should be returned by reference in /home/zaaarf/dev/irl/c/php/ext/reflection/tests/gh22000.php on line 13 +bool(true) From 26cb5b2183e511f70483eeb582f6209c5f064137 Mon Sep 17 00:00:00 2001 From: zaaarf Date: Wed, 13 May 2026 16:28:36 +0200 Subject: [PATCH 6/6] check if it's a reference --- ext/reflection/php_reflection.c | 7 +++++- ext/reflection/tests/gh22000.phpt | 39 ++++++++++++++++++++++++------- 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 3244a39450f1..a43a436ca06f 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -6731,7 +6731,12 @@ ZEND_METHOD(ReflectionProperty, isReadable) zval member; ZVAL_STR(&member, ref->unmangled_name); zend_call_known_instance_method_with_1_params(ce->__isset, obj, return_value, &member); - zend_unwrap_reference(return_value); + + // if it's a reference, unwrap + if (Z_TYPE_P(return_value) == IS_REFERENCE) { + zend_unwrap_reference(return_value); + } + *guard &= ~ZEND_GUARD_PROPERTY_ISSET; OBJ_RELEASE(obj); return; diff --git a/ext/reflection/tests/gh22000.phpt b/ext/reflection/tests/gh22000.phpt index a742030792e8..04c9214e60fe 100644 --- a/ext/reflection/tests/gh22000.phpt +++ b/ext/reflection/tests/gh22000.phpt @@ -2,8 +2,8 @@ GH-22000 - Ensure __isset is not returning a reference in ReflectionProperty::isReadable() --FILE-- c); } + public function __isset($name) { + return $name === 'b'; + } + + public function __get($name) {} +} + +class TestClass2 { + public int $d; + public int $e; + public int $f; + + public function __construct() { + unset($this->e); + unset($this->f); + } + public function &__isset($name) { - return $name === 'c'; + return $name === 'f'; } public function __get($name) {} @@ -28,13 +45,17 @@ function test($class) { } } -test('TestClass'); +test('TestClass1'); +test('TestClass2'); ?> --EXPECTF-- -a from global:bool(false) -b from global: -Notice: Only variable references should be returned by reference in /home/zaaarf/dev/irl/c/php/ext/reflection/tests/gh22000.php on line 13 +a from global:bool(true) +b from global:bool(true) +c from global:bool(false) +d from global:bool(false) +e from global: +Notice: Only variable references should be returned by reference in %s%eext%ereflection%etests%egh22000.php on line %d bool(false) -c from global: -Notice: Only variable references should be returned by reference in /home/zaaarf/dev/irl/c/php/ext/reflection/tests/gh22000.php on line 13 +f from global: +Notice: Only variable references should be returned by reference in %s%eext%ereflection%etests%egh22000.php on line %d bool(true)