From 170f1683fa04b7574a09a76ee65eab07f9de9b61 Mon Sep 17 00:00:00 2001 From: ndossche <7771979+ndossche@users.noreply.github.com> Date: Sun, 8 Nov 2026 15:22:01 +0100 Subject: [PATCH] Fix GH-25314: Assertion in ZEND_DO_FCALL: reference returned by &__get() reaches property hook TMP result Caused by VAR|TMP overhaul. --- Zend/tests/property_hooks/gh25314.phpt | 31 ++++++++++++++++++++++++++ Zend/zend_object_handlers.c | 8 +++++-- 2 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 Zend/tests/property_hooks/gh25314.phpt diff --git a/Zend/tests/property_hooks/gh25314.phpt b/Zend/tests/property_hooks/gh25314.phpt new file mode 100644 index 000000000000..a72bef2d9fb9 --- /dev/null +++ b/Zend/tests/property_hooks/gh25314.phpt @@ -0,0 +1,31 @@ +--TEST-- +GH-25314 (Assertion in ZEND_DO_FCALL: reference returned by &__get() reaches property hook TMP result) +--CREDITS-- +YuanchengJiang +--FILE-- + parent::$d::get(); } +} +class GC extends C { + public int $d { get => parent::$d::get(); } +} + +var_dump((new GC)->d); + +?> +--EXPECTF-- +Notice: Only variable references should be returned by reference in %s on line %d + +Fatal error: Uncaught TypeError: Value of type null returned from GC::__get() must be compatible with unset property GC::$d of type int in %s:%d +Stack trace: +#0 %s(%d): P->$d::get() +#1 %s(%d): C->$d::get() +#2 %s(%d): GC->$d::get() +#3 {main} + thrown in %s on line %d diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 5bc9e4004eb8..a537f6b2a3ea 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -1880,11 +1880,15 @@ static ZEND_FUNCTION(zend_parent_hook_get_trampoline) } zval rv; - const zval *retval = obj->handlers->read_property(obj, prop_name, BP_VAR_R, NULL, &rv); + zval *retval = obj->handlers->read_property(obj, prop_name, BP_VAR_R, NULL, &rv); if (retval == &rv) { RETVAL_COPY_VALUE(retval); + if (UNEXPECTED(Z_ISREF_P(return_value))) { + /* __get() may return by reference. */ + zend_unwrap_reference(return_value); + } } else { - RETVAL_COPY(retval); + RETVAL_COPY_DEREF(retval); } clean: