Commit c4efd209317 for php
commit c4efd2093177d45012412eecc8a97d6759ccbd29
Author: ndossche <7771979+ndossche@users.noreply.github.com>
Date: Sun Sep 20 00:37:35 2026 +0200
Fix OSS-Fuzz #536440507: Immutable class incorrect assertion
For immutable classes, the flag for updated constants lives on the
mutable part (see zend_update_class_constants).
That means the assertion is bogus and can be replaced with a more
complex check via a helper function.
For the case where we perform the flags check, but not as an assertion
but as a proper check, deferring to zend_update_class_constants() is
enough because it already checks the flags correctly itself.
Closes GH-23781.
diff --git a/NEWS b/NEWS
index f61ceb97aa0..da8362ce1ed 100644
--- a/NEWS
+++ b/NEWS
@@ -13,6 +13,8 @@ PHP NEWS
- Core
. Fix GH-21999: GC inconsistency with lazy object, var_dump(), and object
comparison. (Arnaud)
+ . Fixed OSS-Fuzz #536440507 (Immutable class incorrect assertion).
+ (ndossche)
- DOM:
. Fixed use-after-free when re-constructing a DOMXPath whose php:function
diff --git a/Zend/tests/lazy_objects/oss_fuzz_536440507.phpt b/Zend/tests/lazy_objects/oss_fuzz_536440507.phpt
new file mode 100644
index 00000000000..02a1fcc6934
--- /dev/null
+++ b/Zend/tests/lazy_objects/oss_fuzz_536440507.phpt
@@ -0,0 +1,21 @@
+--TEST--
+OSS-Fuzz #536440507 (Immutable class incorrect assertion)
+--EXTENSIONS--
+opcache
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+--FILE--
+<?php
+
+class C {
+ // Something that emits a warning so it can't be folded at compile time
+ public mixed $b = 340282366920938463454151235394913%435650;
+}
+$o = (new ReflectionClass(C::class))->newLazyGhost(function ($obj) {});
+var_dump($o->b);
+
+?>
+--EXPECTF--
+Deprecated: Implicit conversion from float 3.4028236692093845E+32 to int loses precision in %s on line %d
+int(%s)
diff --git a/Zend/zend_lazy_objects.c b/Zend/zend_lazy_objects.c
index 2ca3a5e4656..f7ef547bc9c 100644
--- a/Zend/zend_lazy_objects.c
+++ b/Zend/zend_lazy_objects.c
@@ -179,6 +179,18 @@ bool zend_lazy_object_decr_lazy_props(zend_object *obj)
return info->lazy_properties_count == 0;
}
+/* See zend_update_class_constants(). */
+static zend_always_inline bool zend_class_constants_are_updated(const zend_class_entry *ce) {
+ if (ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED) {
+ return true;
+ }
+ if (ZEND_MAP_PTR(ce->mutable_data)) {
+ const zend_class_mutable_data *mutable_data = ZEND_MAP_PTR_GET_IMM(ce->mutable_data);
+ return mutable_data && (mutable_data->ce_flags & ZEND_ACC_CONSTANTS_UPDATED);
+ }
+ return false;
+}
+
/**
* Making objects lazy
*/
@@ -259,11 +271,9 @@ ZEND_API zend_object *zend_object_make_lazy(zend_object *obj,
return NULL;
}
- if (UNEXPECTED(!(reflection_ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED))) {
- if (UNEXPECTED(zend_update_class_constants(reflection_ce) != SUCCESS)) {
- ZEND_ASSERT(EG(exception));
- return NULL;
- }
+ if (UNEXPECTED(zend_update_class_constants(reflection_ce) != SUCCESS)) {
+ ZEND_ASSERT(EG(exception));
+ return NULL;
}
obj = zend_objects_new(reflection_ce);
@@ -383,7 +393,9 @@ ZEND_API zend_object *zend_lazy_object_mark_as_initialized(zend_object *obj)
zend_class_entry *ce = obj->ce;
- ZEND_ASSERT(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED);
+#if ZEND_DEBUG
+ ZEND_ASSERT(zend_class_constants_are_updated(ce));
+#endif
zval *default_properties_table = CE_DEFAULT_PROPERTIES_TABLE(ce);
zval *properties_table = obj->properties_table;
@@ -579,7 +591,9 @@ ZEND_API zend_object *zend_lazy_object_init(zend_object *obj)
zend_class_entry *ce = obj->ce;
- ZEND_ASSERT(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED);
+#if ZEND_DEBUG
+ ZEND_ASSERT(zend_class_constants_are_updated(ce));
+#endif
if (zend_object_is_lazy_proxy(obj)) {
return zend_lazy_object_init_proxy(obj);