Skip to content

Commit 2cc4532

Browse files
Merge branch 'PHP-8.3' into PHP-8.4
* PHP-8.3: Fix phpGH-19926: reset internal pointer earlier while splicing array while COW violation flag is still set (php#19929)
2 parents 0099779 + 64c1d43 commit 2cc4532

File tree

4 files changed

+47
-2
lines changed

4 files changed

+47
-2
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ PHP NEWS
6868
(alexandre-daubois)
6969
. Fixed bug GH-20043 (array_unique assertion failure with RC1 array
7070
causing an exception on sort). (nielsdos)
71+
. Fixed bug GH-19926 (reset internal pointer earlier while splicing array
72+
while COW violation flag is still set). (alexandre-daubois)
7173

7274
- Streams:
7375
. Fixed bug GH-19248 (Use strerror_r instead of strerror in main).

ext/standard/array.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3497,6 +3497,12 @@ static void php_splice(HashTable *in_hash, zend_long offset, zend_long length, H
34973497
HT_SET_ITERATORS_COUNT(in_hash, 0);
34983498
in_hash->pDestructor = NULL;
34993499

3500+
/* Set internal pointer to 0 directly instead of calling zend_hash_internal_pointer_reset().
3501+
* This avoids the COW violation assertion and delays advancing to the first valid position
3502+
* until after we've switched to the new array structure (out_hash). The iterator will be
3503+
* advanced when actually accessed, at which point it will find valid indexes in the new array. */
3504+
in_hash->nInternalPointer = 0;
3505+
35003506
if (UNEXPECTED(GC_DELREF(in_hash) == 0)) {
35013507
/* Array was completely deallocated during the operation */
35023508
zend_array_destroy(in_hash);
@@ -3515,8 +3521,6 @@ static void php_splice(HashTable *in_hash, zend_long offset, zend_long length, H
35153521
in_hash->nNextFreeElement = out_hash.nNextFreeElement;
35163522
in_hash->arData = out_hash.arData;
35173523
in_hash->pDestructor = out_hash.pDestructor;
3518-
3519-
zend_hash_internal_pointer_reset(in_hash);
35203524
}
35213525
/* }}} */
35223526

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
--TEST--
2+
GH-19926 (Assertion failure zend_hash_internal_pointer_reset_ex)
3+
--FILE--
4+
<?php
5+
class ThrowingDestructor {
6+
function __destruct() {
7+
throw new Exception();
8+
}
9+
}
10+
11+
$arr = [new ThrowingDestructor(), new ThrowingDestructor()];
12+
13+
try {
14+
array_splice($arr, 0, 2);
15+
} catch (Exception $e) {
16+
echo "Exception caught, no assertion failure\n";
17+
}
18+
?>
19+
--EXPECT--
20+
Exception caught, no assertion failure
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
--TEST--
2+
GH-19926 (internal pointer behavior after array_splice)
3+
--FILE--
4+
<?php
5+
$a = [1, 2, 3];
6+
unset($a[0]);
7+
next($a);
8+
9+
echo "Before array_splice: ";
10+
var_dump(current($a));
11+
12+
array_splice($a, 0, 0, [999]);
13+
14+
echo "After array_splice: ";
15+
var_dump(current($a));
16+
?>
17+
--EXPECT--
18+
Before array_splice: int(3)
19+
After array_splice: int(999)

0 commit comments

Comments
 (0)