You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Hooked properties combined with lazy ghosts cause an assertion failure #16615
This issue started over in docker-library/php#1545 because I initially thought this was an issue with the Debian based Docker images for PHP 8.4.0RC, but since then @yosifkit and @tianon helped with realizing that it's not related to Docker at all.
The problem
This is a minimal example of a class with a property that has a set-hook, but specifically no get-hook. When you create a lazy ghost object from this class and try to access its property, PHP should call its initialization function. And it usually does.
However, when PHP was compiled with GCC 12.2.0 (I did not have time to test any other versions) and CFLAGS="-O2", the code throws the following fatal error instead:
Fatal error: Uncaught Error: Typed property Foo::$bar must not be accessed before initialization in /workspace/test.php:15
Stack trace:
#0 {main}
thrown in /workspace/test.php on line 15
Some more information
GCC 12.2.0 is the current version shipped with Debian "Bookworm", hence why this bug appears in the official Docker images. However, I could reproduce it outside of Docker, on a different Linux distribution, but with the same compiler version. So GCC was the common denominator, as far as I could tell.
In the other thread, we suspected that this could be a compiler bug in GCC. However, I'm not well-versed enough in compilers - or the internals of PHP for that matter - to really tell. So I'm leaving this here with all the information I could gather for someone who can.
changed the title [-]Fatal Error in PHP 8.4 when compiled with certain versions of GCC and -O2[/-][+]Hooked properties combined with lazy ghosts cause an assertion failure[/+]on Oct 26, 2024
I think the assertion/assumption is too conservative. This fixes it and makes sense to me although I haven't deep-dived into the internals of lazy objects yet:
diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c
index 17b85dbfe2e..bac128f83aa 100644
--- a/Zend/zend_object_handlers.c+++ b/Zend/zend_object_handlers.c@@ -790,8 +790,8 @@ ZEND_API zval *zend_std_read_property(zend_object *zobj, zend_string *name, int
retval = OBJ_PROP(zobj, prop_info->offset);
if (UNEXPECTED(Z_TYPE_P(retval) == IS_UNDEF)) {
/* As hooked properties can't be unset, the only way to end up with an undef
- * value is via an uninitialized property. */- ZEND_ASSERT(Z_PROP_FLAG_P(retval) == IS_PROP_UNINIT);+ * value is via an uninitialized property or via lazy objects. */+ ZEND_ASSERT(Z_PROP_FLAG_P(retval) == IS_PROP_UNINIT || zend_lazy_object_must_init(zobj));
goto uninit_error;
}
Agreed. An alternative fix would be to assert that Z_PROP_FLAG_P(retval) contains IS_PROP_UNINIT:
diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c
index 17b85dbfe2e..f0868509274 100644
--- a/Zend/zend_object_handlers.c+++ b/Zend/zend_object_handlers.c@@ -791,7 +791,7 @@ ZEND_API zval *zend_std_read_property(zend_object *zobj, zend_string *name, int
if (UNEXPECTED(Z_TYPE_P(retval) == IS_UNDEF)) {
/* As hooked properties can't be unset, the only way to end up with an undef
* value is via an uninitialized property. */
- ZEND_ASSERT(Z_PROP_FLAG_P(retval) == IS_PROP_UNINIT);+ ZEND_ASSERT(Z_PROP_FLAG_P(retval) & IS_PROP_UNINIT);
goto uninit_error;
}
Z_PROP_FLAG_P(retval) in this case is IS_PROP_UNINIT | IS_PROP_LAZY.
Activity
ndossche commentedon Oct 26, 2024
Not a compiler bug, but an assertion failure, which confuses the compiler because assertions are turned into assumes in release builds:
This is likely the same bug as this oss-fuzz bug: https://issues.oss-fuzz.com/issues/373025987
[-]Fatal Error in PHP 8.4 when compiled with certain versions of GCC and -O2[/-][+]Hooked properties combined with lazy ghosts cause an assertion failure[/+]ndossche commentedon Oct 26, 2024
I think the assertion/assumption is too conservative. This fixes it and makes sense to me although I haven't deep-dived into the internals of lazy objects yet:
cc @arnaud-lb
arnaud-lb commentedon Oct 28, 2024
Agreed. An alternative fix would be to assert that
Z_PROP_FLAG_P(retval)containsIS_PROP_UNINIT:Z_PROP_FLAG_P(retval)in this case isIS_PROP_UNINIT | IS_PROP_LAZY.ndossche commentedon Oct 28, 2024
Looks fine too. Idk what is better, I would still prefer though if your patch had the comment updated too so that we know why it's a bitwise and.
Fix assertion failure in zend_std_read_property