diff --git a/ext/spl/spl_array.c b/ext/spl/spl_array.c index b60b3b9c0dac..6a5cbf9f388d 100644 --- a/ext/spl/spl_array.c +++ b/ext/spl/spl_array.c @@ -943,6 +943,19 @@ static void spl_array_set_array(zval *object, spl_array_object *intern, zval *ar } } else { if (Z_OBJ_HT_P(array) == &spl_handler_ArrayObject) { + if (Z_OBJ_P(object) != Z_OBJ_P(array)) { + /* Reject storage that (indirectly) uses this object as its storage. */ + spl_array_object *other = Z_SPLARRAY_P(array); + while (other->ar_flags & SPL_ARRAY_USE_OTHER) { + if (Z_OBJ(other->array) == Z_OBJ_P(object)) { + zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0, + "Cannot use %s as storage of %s, as it would create a cycle", + ZSTR_VAL(Z_OBJCE_P(array)->name), ZSTR_VAL(intern->std.ce->name)); + return; + } + other = Z_SPLARRAY_P(&other->array); + } + } ZVAL_COPY_VALUE(&garbage, &intern->array); if (just_array) { spl_array_object *other = Z_SPLARRAY_P(array); diff --git a/ext/spl/tests/gh24615.phpt b/ext/spl/tests/gh24615.phpt new file mode 100644 index 000000000000..f31f7230eb18 --- /dev/null +++ b/ext/spl/tests/gh24615.phpt @@ -0,0 +1,57 @@ +--TEST-- +GH-24615 (ArrayObject backing storage cycle causes unbounded recursion) +--FILE-- +exchangeArray($b); +} catch (InvalidArgumentException $e) { + echo $e->getMessage(), "\n"; +} +var_dump(count($a), count($b)); + +/* Longer cycle, through an ArrayIterator */ +$c = new ArrayIterator($b); +try { + $a->exchangeArray($c); +} catch (InvalidArgumentException $e) { + echo $e->getMessage(), "\n"; +} +try { + $a->__construct($c); +} catch (InvalidArgumentException $e) { + echo $e->getMessage(), "\n"; +} +var_dump(count($a), count($b), count($c)); + +/* Cycle built by __unserialize() */ +$d = new ArrayObject([2]); +try { + $a->__unserialize([0, $d, []]); + $d->__unserialize([0, $a, []]); +} catch (InvalidArgumentException $e) { + echo $e->getMessage(), "\n"; +} +var_dump(count($a), count($d)); + +/* Using an object as its own storage and sharing storage are still allowed */ +$e = new ArrayObject([3]); +$e->exchangeArray($e); +$f = new ArrayObject($b); +var_dump(count($e), count($f)); +?> +--EXPECT-- +Cannot use ArrayObject as storage of ArrayObject, as it would create a cycle +int(1) +int(1) +Cannot use ArrayIterator as storage of ArrayObject, as it would create a cycle +Cannot use ArrayIterator as storage of ArrayObject, as it would create a cycle +int(1) +int(1) +int(1) +Cannot use ArrayObject as storage of ArrayObject, as it would create a cycle +int(1) +int(1) +int(0) +int(1)