Skip to content

Commit 2210fda

Browse files
committed
Fix use-after-free when ArrayObject sort comparator replaces backing store
spl_array_method() caches the backing HashTable pointer across a user-supplied comparator (uasort/uksort and the sort handlers). The comparator can re-enter __construct() or __unserialize(), which route through spl_array_set_array() and swap intern->array out from under the cached pointer, leaving the post-sort cleanup to release and dereference freed memory. Mirror the nApplyCount guard the other mutators already use so replacing the backing store during a sort throws instead. Closes GH-22310
1 parent f75ae84 commit 2210fda

2 files changed

Lines changed: 38 additions & 0 deletions

File tree

ext/spl/spl_array.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -927,6 +927,10 @@ static zend_result spl_array_skip_protected(spl_array_object *intern, HashTable
927927
static void spl_array_set_array(zval *object, spl_array_object *intern, zval *array, zend_long ar_flags, bool just_array) {
928928
/* Handled by ZPP prior to this, or for __unserialize() before passing to here */
929929
ZEND_ASSERT(Z_TYPE_P(array) == IS_ARRAY || Z_TYPE_P(array) == IS_OBJECT);
930+
if (intern->nApplyCount > 0) {
931+
zend_throw_error(NULL, "Modification of ArrayObject during sorting is prohibited");
932+
return;
933+
}
930934
zval garbage;
931935
ZVAL_UNDEF(&garbage);
932936
if (Z_TYPE_P(array) == IS_ARRAY) {
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
--TEST--
2+
Can't use __construct() to replace the backing store while ArrayObject is being sorted
3+
--FILE--
4+
<?php
5+
6+
$ao = new ArrayObject([1, 2, 3]);
7+
$other = new ArrayObject([4, 5, 6]);
8+
$i = 0;
9+
$ao->uasort(function($a, $b) use ($ao, $other, &$i) {
10+
if ($i++ == 0) {
11+
try {
12+
$ao->__construct($other);
13+
} catch (Error $e) {
14+
echo $e->getMessage(), "\n";
15+
}
16+
}
17+
return $a <=> $b;
18+
});
19+
var_dump($ao);
20+
21+
?>
22+
--EXPECT--
23+
Modification of ArrayObject during sorting is prohibited
24+
object(ArrayObject)#1 (1) {
25+
["storage":"ArrayObject":private]=>
26+
array(3) {
27+
[0]=>
28+
int(1)
29+
[1]=>
30+
int(2)
31+
[2]=>
32+
int(3)
33+
}
34+
}

0 commit comments

Comments
 (0)