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
This commit is contained in:
Arnaud Le Blanc 2024-09-11 19:36:31 +02:00
parent 30dd291628
commit 64081d1380
No known key found for this signature in database
GPG Key ID: 0098C05DD15ABC13
5 changed files with 33 additions and 65 deletions

2
NEWS
View File

@ -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

View File

@ -1,27 +0,0 @@
--TEST--
Lazy objects: Class constants are updated before initialization
--FILE--
<?php
class C {
public stdClass $a = FOO;
}
$reflector = new ReflectionClass(C::class);
$c = $reflector->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) {
}

View File

@ -1,26 +0,0 @@
--TEST--
Lazy objects: Class constants are updated before initialization: update constant failure
--FILE--
<?php
class C {
public C $a = FOO;
}
$reflector = new ReflectionClass(C::class);
$c = $reflector->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

View File

@ -0,0 +1,22 @@
--TEST--
oss-fuzz #71407
--FILE--
<?php
class C {
public $x=x;
}
$reflector = new ReflectionClass(C::class);
try {
$obj = $reflector->newLazyGhost(function() {});
clone $obj;
} catch (Error $e) {
printf("%s: %s\n", $e::class, $e->getMessage());
}
?>
--EXPECT--
Error: Undefined constant "x"

View File

@ -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);