From 64081d1380f179d4ee0ae1e6e167cb3efd398290 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Wed, 11 Sep 2024 19:36:31 +0200 Subject: [PATCH] Lazy objects: Update class constants earlier If a lazy object is created for a class whose constants can not be updated, then we have created an instance of a non-instantiable class. This breaks the expectations of clone. Here I ensure that a class has its constants updated before creating a lazy instance of it. Fixes OSS-Fuzz #71407 Closes GH-15856 --- NEWS | 2 ++ Zend/tests/lazy_objects/init_ast_const.phpt | 27 ------------------- .../lazy_objects/init_ast_const_failure.phpt | 26 ------------------ Zend/tests/lazy_objects/oss_fuzz_71407.phpt | 22 +++++++++++++++ Zend/zend_lazy_objects.c | 21 +++++++-------- 5 files changed, 33 insertions(+), 65 deletions(-) delete mode 100644 Zend/tests/lazy_objects/init_ast_const.phpt delete mode 100644 Zend/tests/lazy_objects/init_ast_const_failure.phpt create mode 100644 Zend/tests/lazy_objects/oss_fuzz_71407.phpt diff --git a/NEWS b/NEWS index 378c8ec152f..025b71e9553 100644 --- a/NEWS +++ b/NEWS @@ -17,6 +17,8 @@ PHP NEWS . Fixed bug GH-16371 (Assertion failure in Zend/zend_weakrefs.c:646). (Arnaud) . Fixed missing error when adding asymmetric visibility to static properties. (ilutov) + . Fixed bug OSS-Fuzz #71407 (Null-dereference WRITE in + zend_lazy_object_clone). (Arnaud) - Curl: . Fixed bug GH-16302 (CurlMultiHandle holds a reference to CurlHandle if diff --git a/Zend/tests/lazy_objects/init_ast_const.phpt b/Zend/tests/lazy_objects/init_ast_const.phpt deleted file mode 100644 index 018c46e4915..00000000000 --- a/Zend/tests/lazy_objects/init_ast_const.phpt +++ /dev/null @@ -1,27 +0,0 @@ ---TEST-- -Lazy objects: Class constants are updated before initialization ---FILE-- -newLazyGhost(function () { }); - -function f() { - define('FOO', new stdClass); -} - -f(); - -try { - var_dump($c->a); -} catch (\Error $e) { - printf("%s: %s\n", $e::class, $e->getMessage()); -} - ---EXPECTF-- -object(stdClass)#%d (0) { -} diff --git a/Zend/tests/lazy_objects/init_ast_const_failure.phpt b/Zend/tests/lazy_objects/init_ast_const_failure.phpt deleted file mode 100644 index d3f1ca0f35f..00000000000 --- a/Zend/tests/lazy_objects/init_ast_const_failure.phpt +++ /dev/null @@ -1,26 +0,0 @@ ---TEST-- -Lazy objects: Class constants are updated before initialization: update constant failure ---FILE-- -newLazyGhost(function () { }); - -function f() { - define('FOO', new stdClass); -} - -f(); - -try { - var_dump($c->a); -} catch (\Error $e) { - printf("%s: %s\n", $e::class, $e->getMessage()); -} - ---EXPECT-- -TypeError: Cannot assign stdClass to property C::$a of type C diff --git a/Zend/tests/lazy_objects/oss_fuzz_71407.phpt b/Zend/tests/lazy_objects/oss_fuzz_71407.phpt new file mode 100644 index 00000000000..09b4febcde2 --- /dev/null +++ b/Zend/tests/lazy_objects/oss_fuzz_71407.phpt @@ -0,0 +1,22 @@ +--TEST-- +oss-fuzz #71407 +--FILE-- +newLazyGhost(function() {}); + clone $obj; +} catch (Error $e) { + printf("%s: %s\n", $e::class, $e->getMessage()); +} + + +?> +--EXPECT-- +Error: Undefined constant "x" diff --git a/Zend/zend_lazy_objects.c b/Zend/zend_lazy_objects.c index 94be396f580..e6dd6144d4b 100644 --- a/Zend/zend_lazy_objects.c +++ b/Zend/zend_lazy_objects.c @@ -259,6 +259,13 @@ 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; + } + } + obj = zend_objects_new(reflection_ce); for (int i = 0; i < obj->ce->default_properties_count; i++) { @@ -373,12 +380,7 @@ ZEND_API zend_object *zend_lazy_object_mark_as_initialized(zend_object *obj) zend_class_entry *ce = obj->ce; - if (UNEXPECTED(!(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED))) { - if (UNEXPECTED(zend_update_class_constants(ce) != SUCCESS)) { - ZEND_ASSERT(EG(exception)); - return NULL; - } - } + ZEND_ASSERT(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED); zval *default_properties_table = CE_DEFAULT_PROPERTIES_TABLE(ce); zval *properties_table = obj->properties_table; @@ -568,12 +570,7 @@ ZEND_API zend_object *zend_lazy_object_init(zend_object *obj) zend_class_entry *ce = obj->ce; - if (UNEXPECTED(!(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED))) { - if (UNEXPECTED(zend_update_class_constants(ce) != SUCCESS)) { - ZEND_ASSERT(EG(exception)); - return NULL; - } - } + ZEND_ASSERT(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED); if (zend_object_is_lazy_proxy(obj)) { return zend_lazy_object_init_proxy(obj);