Skip to content

Commit 8e3c0c1

Browse files
committed
Fix GH-16957: Assertion failure in array_shift with self-referencing array
We have an RC1 violation because we're immediately dereferencing and copying the resulting array in the test case. Instead, transfer the lifetime using ZVAL_COPY_VALUE and unwrap only after the internal iterator is reset.
1 parent 3c3ec0e commit 8e3c0c1

File tree

2 files changed

+49
-2
lines changed

2 files changed

+49
-2
lines changed

ext/standard/array.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3513,7 +3513,8 @@ PHP_FUNCTION(array_shift)
35133513
}
35143514
idx++;
35153515
}
3516-
RETVAL_COPY_DEREF(val);
3516+
ZVAL_COPY_VALUE(return_value, val);
3517+
ZVAL_UNDEF(val);
35173518

35183519
/* Delete the first value */
35193520
zend_hash_packed_del_val(Z_ARRVAL_P(stack), val);
@@ -3567,7 +3568,8 @@ PHP_FUNCTION(array_shift)
35673568
}
35683569
idx++;
35693570
}
3570-
RETVAL_COPY_DEREF(val);
3571+
ZVAL_COPY_VALUE(return_value, val);
3572+
ZVAL_UNDEF(val);
35713573

35723574
/* Delete the first value */
35733575
zend_hash_del_bucket(Z_ARRVAL_P(stack), p);
@@ -3591,6 +3593,10 @@ PHP_FUNCTION(array_shift)
35913593
}
35923594

35933595
zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack));
3596+
3597+
if (Z_ISREF_P(return_value)) {
3598+
zend_unwrap_reference(return_value);
3599+
}
35943600
}
35953601
/* }}} */
35963602

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
--TEST--
2+
GH-16957 (Assertion failure in array_shift with self-referencing array)
3+
--FILE--
4+
<?php
5+
$new_array = array(&$new_array, 1, 'two');
6+
var_dump($shifted = array_shift($new_array));
7+
var_dump($new_array);
8+
var_dump($new_array === $shifted);
9+
10+
$new_array2 = array(&$new_array2, 2 => 1, 300 => 'two');
11+
var_dump($shifted = array_shift($new_array2));
12+
var_dump($new_array2);
13+
var_dump($new_array2 === $shifted);
14+
?>
15+
--EXPECT--
16+
array(2) {
17+
[0]=>
18+
int(1)
19+
[1]=>
20+
string(3) "two"
21+
}
22+
array(2) {
23+
[0]=>
24+
int(1)
25+
[1]=>
26+
string(3) "two"
27+
}
28+
bool(true)
29+
array(2) {
30+
[0]=>
31+
int(1)
32+
[1]=>
33+
string(3) "two"
34+
}
35+
array(2) {
36+
[0]=>
37+
int(1)
38+
[1]=>
39+
string(3) "two"
40+
}
41+
bool(true)

0 commit comments

Comments
 (0)