Skip to content

Commit 60181f4

Browse files
Fix GH-16649: Avoid UAF when using array_splice
1 parent bd2766c commit 60181f4

File tree

3 files changed

+58
-5
lines changed

3 files changed

+58
-5
lines changed

NEWS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ PHP NEWS
7373
. Fix theoretical issues with hrtime() not being available. (nielsdos)
7474
. Fixed bug GH-19300 (Nested array_multisort invocation with error breaks).
7575
(nielsdos)
76+
. Fixed bug GH-16649 (UAF during array_splice). (alexandre-daubois)
7677

7778
- Windows:
7879
. Free opened_path when opened_path_len >= MAXPATHLEN. (dixyes)

Zend/tests/gh16649.phpt

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
--TEST--
2+
GH-16649: Use-after-free during array_splice
3+
--FILE--
4+
<?php
5+
6+
function resize_arr() {
7+
global $arr;
8+
for ($i = 0; $i < 10; $i++) {
9+
$arr[$i] = $i;
10+
}
11+
}
12+
13+
class C {
14+
function __destruct() {
15+
resize_arr();
16+
return "3";
17+
}
18+
}
19+
20+
$arr = ["a" => "1", "3" => new C, "2" => "2"];
21+
22+
array_splice($arr, 1, 2);
23+
24+
var_dump($arr);
25+
26+
?>
27+
--EXPECT--
28+
array(9) {
29+
["a"]=>
30+
string(1) "1"
31+
[0]=>
32+
int(1)
33+
[1]=>
34+
int(3)
35+
[2]=>
36+
int(4)
37+
[3]=>
38+
int(5)
39+
[4]=>
40+
int(6)
41+
[5]=>
42+
int(7)
43+
[6]=>
44+
int(8)
45+
[7]=>
46+
int(9)
47+
}

ext/standard/array.c

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3252,7 +3252,8 @@ static void php_splice(HashTable *in_hash, zend_long offset, zend_long length, H
32523252

32533253
/* If hash for removed entries exists, go until offset+length and copy the entries to it */
32543254
if (removed != NULL) {
3255-
for ( ; pos - offset < length && idx < in_hash->nNumUsed; idx++, entry++) {
3255+
for ( ; pos - offset < length && idx < in_hash->nNumUsed; idx++) {
3256+
entry = in_hash->arPacked + idx;
32563257
if (Z_TYPE_P(entry) == IS_UNDEF) continue;
32573258
pos++;
32583259
Z_TRY_ADDREF_P(entry);
@@ -3262,7 +3263,8 @@ static void php_splice(HashTable *in_hash, zend_long offset, zend_long length, H
32623263
} else { /* otherwise just skip those entries */
32633264
zend_long pos2 = pos;
32643265

3265-
for ( ; pos2 - offset < length && idx < in_hash->nNumUsed; idx++, entry++) {
3266+
for ( ; pos2 - offset < length && idx < in_hash->nNumUsed; idx++) {
3267+
entry = in_hash->arPacked + idx;
32663268
if (Z_TYPE_P(entry) == IS_UNDEF) continue;
32673269
pos2++;
32683270
zend_hash_packed_del_val(in_hash, entry);
@@ -3317,7 +3319,8 @@ static void php_splice(HashTable *in_hash, zend_long offset, zend_long length, H
33173319

33183320
/* If hash for removed entries exists, go until offset+length and copy the entries to it */
33193321
if (removed != NULL) {
3320-
for ( ; pos - offset < length && idx < in_hash->nNumUsed; idx++, p++) {
3322+
for ( ; pos - offset < length && idx < in_hash->nNumUsed; idx++) {
3323+
p = in_hash->arData + idx;
33213324
if (Z_TYPE(p->val) == IS_UNDEF) continue;
33223325
pos++;
33233326
entry = &p->val;
@@ -3332,7 +3335,8 @@ static void php_splice(HashTable *in_hash, zend_long offset, zend_long length, H
33323335
} else { /* otherwise just skip those entries */
33333336
zend_long pos2 = pos;
33343337

3335-
for ( ; pos2 - offset < length && idx < in_hash->nNumUsed; idx++, p++) {
3338+
for ( ; pos2 - offset < length && idx < in_hash->nNumUsed; idx++) {
3339+
p = in_hash->arData + idx;
33363340
if (Z_TYPE(p->val) == IS_UNDEF) continue;
33373341
pos2++;
33383342
zend_hash_del_bucket(in_hash, p);
@@ -3350,7 +3354,8 @@ static void php_splice(HashTable *in_hash, zend_long offset, zend_long length, H
33503354
}
33513355

33523356
/* Copy the remaining input hash entries to the output hash */
3353-
for ( ; idx < in_hash->nNumUsed ; idx++, p++) {
3357+
for ( ; idx < in_hash->nNumUsed ; idx++) {
3358+
p = in_hash->arData + idx;
33543359
if (Z_TYPE(p->val) == IS_UNDEF) continue;
33553360
entry = &p->val;
33563361
if (p->key == NULL) {

0 commit comments

Comments
 (0)