Skip to content
Merged
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
29 changes: 29 additions & 0 deletions Zend/tests/fibers/gh19983.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
--TEST--
GH-19983 (GC Assertion Failure with fibers, generators and destructors)
--SKIPIF--
<?php if (PHP_INT_SIZE < 8) die("skip 64-bit only - fiber stacks exhaust 32-bit address space"); ?>
--INI--
memory_limit=128M
--FILE--
<?php
class a
{
function __destruct()
{
$gen = (function () {
$from = (function () {
$cv = [new a];
Fiber::suspend();
})();
yield from $from;
})();
$fiber = new Fiber(function () use ($gen, &$fiber) {
$gen->current();
});
$fiber->start();
}
}
new a;
?>
--EXPECTF--
Fatal error: Allowed memory size of %d bytes exhausted%s
4 changes: 3 additions & 1 deletion Zend/zend_gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1953,13 +1953,15 @@ static zend_never_inline void gc_call_destructors_in_fiber(void)
GC_G(dtor_idx) = GC_FIRST_ROOT;
GC_G(dtor_end) = GC_G(first_unused);

zend_object *exception = NULL;
remember_prev_exception(&exception);

if (UNEXPECTED(!fiber)) {
fiber = gc_create_destructor_fiber();
} else {
zend_fiber_resume(fiber, NULL, NULL);
}

zend_object *exception = NULL;
remember_prev_exception(&exception);

for (;;) {
Expand Down
167 changes: 102 additions & 65 deletions ext/spl/php_spl.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,43 @@ static zend_class_entry * spl_find_ce_by_name(zend_string *name, bool autoload)
return ce;
}

static void spl_add_class_name(HashTable *list, zend_string *name)
{
zval t;
ZVAL_STR_COPY(&t, name);
zend_hash_add(list, name, &t);
}

static void spl_add_interfaces(HashTable *list, const zend_class_entry *pce)
{
if (pce->num_interfaces) {
ZEND_ASSERT(pce->ce_flags & ZEND_ACC_LINKED);
for (uint32_t num_interfaces = 0; num_interfaces < pce->num_interfaces; num_interfaces++) {
spl_add_class_name(list, pce->interfaces[num_interfaces]->name);
}
}
}

static void spl_add_traits(HashTable *list, const zend_class_entry *pce)
{
for (uint32_t num_traits = 0; num_traits < pce->num_traits; num_traits++) {
spl_add_class_name(list, pce->trait_names[num_traits].name);
}
}

static void spl_add_classes(HashTable *list, const zend_class_entry *pce, bool only_classes, bool only_interfaces)
{
ZEND_ASSERT(pce);
ZEND_ASSERT(!(only_classes && only_interfaces) && "Cannot have both only classes and only interfaces be enabled");
if (
(only_classes && (pce->ce_flags & ZEND_ACC_INTERFACE) == ZEND_ACC_INTERFACE)
|| (only_interfaces && (pce->ce_flags & ZEND_ACC_INTERFACE) == 0)
) {
return;
}
spl_add_class_name(list, pce->name);
}

/* {{{ Return an array containing the names of all parent classes */
PHP_FUNCTION(class_parents)
{
Expand Down Expand Up @@ -88,7 +125,7 @@ PHP_FUNCTION(class_parents)
array_init(return_value);
const zend_class_entry *parent_class = ce->parent;
while (parent_class) {
spl_add_class_name(return_value, parent_class, 0, 0);
spl_add_class_name(Z_ARR_P(return_value), parent_class->name);
parent_class = parent_class->parent;
}
}
Expand Down Expand Up @@ -119,7 +156,7 @@ PHP_FUNCTION(class_implements)
}

array_init(return_value);
spl_add_interfaces(return_value, ce, 1, ZEND_ACC_INTERFACE);
spl_add_interfaces(Z_ARR_P(return_value), ce);
}
/* }}} */

Expand Down Expand Up @@ -148,69 +185,69 @@ PHP_FUNCTION(class_uses)
}

array_init(return_value);
spl_add_traits(return_value, ce, 1, ZEND_ACC_TRAIT);
spl_add_traits(Z_ARR_P(return_value), ce);
}
/* }}} */

#define SPL_ADD_CLASS(class_name, z_list, sub, allow, ce_flags) \
spl_add_classes(spl_ce_ ## class_name, z_list, sub, allow, ce_flags)

#define SPL_LIST_CLASSES(z_list, sub, allow, ce_flags) \
SPL_ADD_CLASS(AppendIterator, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(ArrayIterator, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(ArrayObject, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(BadFunctionCallException, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(BadMethodCallException, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(CachingIterator, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(CallbackFilterIterator, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(DirectoryIterator, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(DomainException, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(EmptyIterator, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(FilesystemIterator, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(FilterIterator, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(GlobIterator, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(InfiniteIterator, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(InvalidArgumentException, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(IteratorIterator, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(LengthException, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(LimitIterator, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(LogicException, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(MultipleIterator, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(NoRewindIterator, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(OuterIterator, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(OutOfBoundsException, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(OutOfRangeException, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(OverflowException, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(ParentIterator, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(RangeException, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(RecursiveArrayIterator, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(RecursiveCachingIterator, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(RecursiveCallbackFilterIterator, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(RecursiveDirectoryIterator, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(RecursiveFilterIterator, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(RecursiveIterator, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(RecursiveIteratorIterator, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(RecursiveRegexIterator, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(RecursiveTreeIterator, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(RegexIterator, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(RuntimeException, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(SeekableIterator, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(SplDoublyLinkedList, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(SplFileInfo, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(SplFileObject, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(SplFixedArray, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(SplHeap, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(SplMinHeap, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(SplMaxHeap, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(SplObjectStorage, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(SplObserver, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(SplPriorityQueue, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(SplQueue, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(SplStack, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(SplSubject, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(SplTempFileObject, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(UnderflowException, z_list, sub, allow, ce_flags); \
SPL_ADD_CLASS(UnexpectedValueException, z_list, sub, allow, ce_flags); \
#define SPL_ADD_CLASS(class_name, z_list, only_classes, only_interfaces) \
spl_add_classes(Z_ARR_P(z_list), spl_ce_ ## class_name, only_classes, only_interfaces)

#define SPL_LIST_CLASSES(z_list, only_classes, only_interfaces) \
SPL_ADD_CLASS(AppendIterator, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(ArrayIterator, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(ArrayObject, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(BadFunctionCallException, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(BadMethodCallException, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(CachingIterator, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(CallbackFilterIterator, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(DirectoryIterator, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(DomainException, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(EmptyIterator, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(FilesystemIterator, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(FilterIterator, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(GlobIterator, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(InfiniteIterator, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(InvalidArgumentException, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(IteratorIterator, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(LengthException, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(LimitIterator, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(LogicException, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(MultipleIterator, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(NoRewindIterator, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(OuterIterator, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(OutOfBoundsException, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(OutOfRangeException, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(OverflowException, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(ParentIterator, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(RangeException, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(RecursiveArrayIterator, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(RecursiveCachingIterator, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(RecursiveCallbackFilterIterator, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(RecursiveDirectoryIterator, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(RecursiveFilterIterator, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(RecursiveIterator, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(RecursiveIteratorIterator, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(RecursiveRegexIterator, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(RecursiveTreeIterator, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(RegexIterator, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(RuntimeException, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(SeekableIterator, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(SplDoublyLinkedList, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(SplFileInfo, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(SplFileObject, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(SplFixedArray, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(SplHeap, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(SplMinHeap, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(SplMaxHeap, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(SplObjectStorage, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(SplObserver, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(SplPriorityQueue, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(SplQueue, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(SplStack, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(SplSubject, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(SplTempFileObject, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(UnderflowException, z_list, only_classes, only_interfaces); \
SPL_ADD_CLASS(UnexpectedValueException, z_list, only_classes, only_interfaces); \

/* {{{ Return an array containing the names of all classes and interfaces defined in SPL */
PHP_FUNCTION(spl_classes)
Expand All @@ -219,7 +256,7 @@ PHP_FUNCTION(spl_classes)

array_init(return_value);

SPL_LIST_CLASSES(return_value, 0, 0, 0)
SPL_LIST_CLASSES(return_value, false, false)
}
/* }}} */

Expand Down Expand Up @@ -486,7 +523,7 @@ PHP_MINFO_FUNCTION(spl)
php_info_print_table_row(2, "SPL support", "enabled");

array_init(&list);
SPL_LIST_CLASSES(&list, 0, 1, ZEND_ACC_INTERFACE)
SPL_LIST_CLASSES(&list, false, true)
strg = estrdup("");
ZEND_HASH_MAP_FOREACH_VAL(Z_ARRVAL_P(&list), zv) {
spl_build_class_list_string(zv, &strg);
Expand All @@ -496,7 +533,7 @@ PHP_MINFO_FUNCTION(spl)
efree(strg);

array_init(&list);
SPL_LIST_CLASSES(&list, 0, -1, ZEND_ACC_INTERFACE)
SPL_LIST_CLASSES(&list, true, false)
strg = estrdup("");
ZEND_HASH_MAP_FOREACH_VAL(Z_ARRVAL_P(&list), zv) {
spl_build_class_list_string(zv, &strg);
Expand Down
57 changes: 1 addition & 56 deletions ext/spl/spl_array.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,6 @@ typedef struct _spl_array_object {
uint32_t ht_iter;
int ar_flags;
unsigned char nApplyCount;
bool is_child;
Bucket *bucket;
zend_function *fptr_offset_get;
zend_function *fptr_offset_set;
zend_function *fptr_offset_has;
Expand Down Expand Up @@ -166,8 +164,6 @@ static zend_object *spl_array_object_new_ex(zend_class_entry *class_type, zend_o
object_properties_init(&intern->std, class_type);

intern->ar_flags = 0;
intern->is_child = false;
intern->bucket = NULL;
intern->ce_get_iterator = spl_ce_ArrayIterator;
if (orig) {
spl_array_object *other = spl_array_from_obj(orig);
Expand Down Expand Up @@ -465,22 +461,6 @@ static zval *spl_array_read_dimension(zend_object *object, zval *offset, int typ
return spl_array_read_dimension_ex(1, object, offset, type, rv);
} /* }}} */

/*
* The assertion(HT_ASSERT_RC1(ht)) failed because the refcount was increased manually when intern->is_child is true.
* We have to set the refcount to 1 to make assertion success and restore the refcount to the original value after
* modifying the array when intern->is_child is true.
*/
static uint32_t spl_array_set_refcount(bool is_child, HashTable *ht, uint32_t refcount) /* {{{ */
{
uint32_t old_refcount = 0;
if (is_child) {
old_refcount = GC_REFCOUNT(ht);
GC_SET_REFCOUNT(ht, refcount);
}

return old_refcount;
} /* }}} */

static void spl_array_write_dimension_ex(int check_inherited, zend_object *object, zval *offset, zval *value) /* {{{ */
{
spl_array_object *intern = spl_array_from_obj(object);
Expand All @@ -504,19 +484,12 @@ static void spl_array_write_dimension_ex(int check_inherited, zend_object *objec
}

Z_TRY_ADDREF_P(value);

uint32_t refcount = 0;
if (!offset || Z_TYPE_P(offset) == IS_NULL) {
ht = spl_array_get_hash_table(intern);
if (UNEXPECTED(ht == intern->sentinel_array)) {
return;
}
refcount = spl_array_set_refcount(intern->is_child, ht, 1);
zend_hash_next_index_insert(ht, value);

if (refcount) {
spl_array_set_refcount(intern->is_child, ht, refcount);
}
return;
}

Expand All @@ -531,17 +504,13 @@ static void spl_array_write_dimension_ex(int check_inherited, zend_object *objec
spl_hash_key_release(&key);
return;
}
refcount = spl_array_set_refcount(intern->is_child, ht, 1);

if (key.key) {
zend_hash_update_ind(ht, key.key, value);
spl_hash_key_release(&key);
} else {
zend_hash_index_update(ht, key.h, value);
}

if (refcount) {
spl_array_set_refcount(intern->is_child, ht, refcount);
}
} /* }}} */

static void spl_array_write_dimension(zend_object *object, zval *offset, zval *value) /* {{{ */
Expand Down Expand Up @@ -571,8 +540,6 @@ static void spl_array_unset_dimension_ex(int check_inherited, zend_object *objec
}

ht = spl_array_get_hash_table(intern);
uint32_t refcount = spl_array_set_refcount(intern->is_child, ht, 1);

if (key.key) {
zval *data = zend_hash_find(ht, key.key);
if (data) {
Expand All @@ -597,10 +564,6 @@ static void spl_array_unset_dimension_ex(int check_inherited, zend_object *objec
} else {
zend_hash_index_del(ht, key.h);
}

if (refcount) {
spl_array_set_refcount(intern->is_child, ht, refcount);
}
} /* }}} */

static void spl_array_unset_dimension(zend_object *object, zval *offset) /* {{{ */
Expand Down Expand Up @@ -972,15 +935,6 @@ static void spl_array_set_array(zval *object, spl_array_object *intern, zval *ar
} else {
//??? TODO: try to avoid array duplication
ZVAL_ARR(&intern->array, zend_array_dup(Z_ARR_P(array)));

if (intern->is_child) {
Z_TRY_DELREF(intern->bucket->val);
/*
* replace bucket->val with copied array, so the changes between
* parent and child object can affect each other.
*/
ZVAL_COPY(&intern->bucket->val, &intern->array);
}
}
} else {
php_error_docref(NULL, E_DEPRECATED,
Expand Down Expand Up @@ -1850,15 +1804,6 @@ PHP_METHOD(RecursiveArrayIterator, hasChildren)
static void spl_instantiate_child_arg(zend_class_entry *pce, zval *retval, zval *arg1, zval *arg2) /* {{{ */
{
object_init_ex(retval, pce);
spl_array_object *new_intern = Z_SPLARRAY_P(retval);
/*
* set new_intern->is_child is true to indicate that the object was created by
* RecursiveArrayIterator::getChildren() method.
*/
new_intern->is_child = true;

/* find the bucket of parent object. */
new_intern->bucket = (Bucket *)((char *)(arg1) - XtOffsetOf(Bucket, val));;
zend_call_known_instance_method_with_2_params(pce->constructor, Z_OBJ_P(retval), NULL, arg1, arg2);
}
/* }}} */
Expand Down
Loading