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
2 changes: 2 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ PHP NEWS
(alexandre-daubois)
. Fixed bug GH-20043 (array_unique assertion failure with RC1 array
causing an exception on sort). (nielsdos)
. Fixed bug GH-19926 (reset internal pointer earlier while splicing array
while COW violation flag is still set). (alexandre-daubois)

- Streams:
. Fixed bug GH-19248 (Use strerror_r instead of strerror in main).
Expand Down
8 changes: 6 additions & 2 deletions ext/standard/array.c
Original file line number Diff line number Diff line change
Expand Up @@ -3376,6 +3376,12 @@ static void php_splice(HashTable *in_hash, zend_long offset, zend_long length, H
HT_SET_ITERATORS_COUNT(in_hash, 0);
in_hash->pDestructor = NULL;

/* Set internal pointer to 0 directly instead of calling zend_hash_internal_pointer_reset().
* This avoids the COW violation assertion and delays advancing to the first valid position
* until after we've switched to the new array structure (out_hash). The iterator will be
* advanced when actually accessed, at which point it will find valid indexes in the new array. */
in_hash->nInternalPointer = 0;

if (UNEXPECTED(GC_DELREF(in_hash) == 0)) {
/* Array was completely deallocated during the operation */
zend_array_destroy(in_hash);
Expand All @@ -3394,8 +3400,6 @@ static void php_splice(HashTable *in_hash, zend_long offset, zend_long length, H
in_hash->nNextFreeElement = out_hash.nNextFreeElement;
in_hash->arData = out_hash.arData;
in_hash->pDestructor = out_hash.pDestructor;

zend_hash_internal_pointer_reset(in_hash);
}
/* }}} */

Expand Down
20 changes: 20 additions & 0 deletions ext/standard/tests/array/gh19926.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
--TEST--
GH-19926 (Assertion failure zend_hash_internal_pointer_reset_ex)
--FILE--
<?php
class ThrowingDestructor {
function __destruct() {
throw new Exception();
}
}

$arr = [new ThrowingDestructor(), new ThrowingDestructor()];

try {
array_splice($arr, 0, 2);
} catch (Exception $e) {
echo "Exception caught, no assertion failure\n";
}
?>
--EXPECT--
Exception caught, no assertion failure
19 changes: 19 additions & 0 deletions ext/standard/tests/array/gh19926_pointer.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
--TEST--
GH-19926 (internal pointer behavior after array_splice)
--FILE--
<?php
$a = [1, 2, 3];
unset($a[0]);
next($a);

echo "Before array_splice: ";
var_dump(current($a));

array_splice($a, 0, 0, [999]);

echo "After array_splice: ";
var_dump(current($a));
?>
--EXPECT--
Before array_splice: int(3)
After array_splice: int(999)
Loading