Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ PHP NEWS
for grouping. (Klaas Skelte van der Werf)
. Fixed bug GH-24265 (Unwind/GracefulExit discarded in
zend_fiber_execute()). (David Carlier)
. Fixed bug GH-24715 (Assertion failure in zend_lazy_object_get_properties()
when releasing the initializer resets the object as lazy).
(Lazizbek Ergashev)

- Date:
. Fixed bug GH-20503 (Assertion failure with ext/date DateInterval property
Expand Down
44 changes: 44 additions & 0 deletions Zend/tests/lazy_objects/gh24715-001.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
--TEST--
GH-24715: Object reset as lazy by a destructor triggered when releasing the initializer
--FILE--
<?php

class C {
public $a;
}

class D {
public $self;
public function __construct() {
$this->self = $this;
}
public function __destruct() {
global $obj, $reflector;
try {
$reflector->resetAsLazyGhost($obj, function () {});
} catch (Error $e) {
printf("%s: %s\n", $e::class, $e->getMessage());
}
}
}

new D();

$reflector = new ReflectionClass(C::class);
$obj = $reflector->newLazyGhost(new class {
public function __invoke($obj) {
$obj->a = 1;
}
public function __destruct() {
gc_collect_cycles();
}
});

var_dump(serialize($obj));
var_dump($reflector->isUninitializedLazyObject($obj));

?>
--EXPECT--
Error: Can not reset an object while it is being initialized
string(24) "O:1:"C":1:{s:1:"a";i:1;}"
bool(false)
33 changes: 33 additions & 0 deletions Zend/tests/lazy_objects/gh24715-002.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
--TEST--
GH-24715 002: Object reset as lazy by a destructor triggered when releasing the initializer in markLazyObjectAsInitialized()
--FILE--
<?php

class C {
public $a;
}

class D {
public function __destruct() {
global $obj, $reflector;
try {
$reflector->resetAsLazyGhost($obj, function () {});
} catch (Error $e) {
printf("%s: %s\n", $e::class, $e->getMessage());
}
}
}

$reflector = new ReflectionClass(C::class);
$d = new D();
$obj = $reflector->newLazyGhost(function ($obj) use ($d) {});
unset($d);

var_dump($reflector->markLazyObjectAsInitialized($obj) === $obj);
var_dump($reflector->isUninitializedLazyObject($obj));

?>
--EXPECT--
Error: Can not reset an object while it is being initialized
bool(true)
bool(false)
21 changes: 20 additions & 1 deletion Zend/zend_lazy_objects.c
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,9 @@ static void zend_lazy_object_info_dtor_func(zval *pElement)
zend_object_release(info->u.instance);
} else {
zval_ptr_dtor(&info->u.initializer.zv);
zend_fcc_dtor(&info->u.initializer.fcc);
if (ZEND_FCC_INITIALIZED(info->u.initializer.fcc)) {
zend_fcc_dtor(&info->u.initializer.fcc);
}
}

efree(info);
Expand Down Expand Up @@ -159,6 +161,20 @@ zend_lazy_object_flags_t zend_lazy_object_get_flags(zend_object *obj)
return zend_lazy_object_get_info(obj)->flags;
}

/* Release the initializer while the info is still registered, so that
* destructors triggered by this can not reset the object */
static void zend_lazy_object_release_initializer(zend_object *obj)
{
zend_lazy_object_info *info = zend_hash_index_find_ptr(&EG(lazy_objects_store).infos, obj->handle);
ZEND_ASSERT(info && !(info->flags & ZEND_LAZY_OBJECT_INITIALIZED));

zval initializer_zv;
ZVAL_COPY_VALUE(&initializer_zv, &info->u.initializer.zv);
ZVAL_UNDEF(&info->u.initializer.zv);
zend_fcc_dtor(&info->u.initializer.fcc);
zval_ptr_dtor(&initializer_zv);
}

void zend_lazy_object_del_info(zend_object *obj)
{
zend_result res = zend_hash_index_del(&EG(lazy_objects_store).infos, obj->handle);
Expand Down Expand Up @@ -410,6 +426,7 @@ ZEND_API zend_object *zend_lazy_object_mark_as_initialized(zend_object *obj)
}
}

zend_lazy_object_release_initializer(obj);
zend_lazy_object_del_info(obj);

return obj;
Expand Down Expand Up @@ -670,6 +687,8 @@ ZEND_API zend_object *zend_lazy_object_init(zend_object *obj)
zend_release_properties(properties_snapshot);
}

zend_lazy_object_release_initializer(obj);

/* Must be very last in this function, for the
* zend_lazy_object_has_stale_info() check */
zend_lazy_object_del_info(obj);
Expand Down
Loading