Skip to content

Commit a311f05

Browse files
committed
Merge branch 'PHP-8.4' into PHP-8.5
* PHP-8.4: Fix use-after-free in Collator::sort() with a mutating comparator
2 parents 97fc3bf + 5fa74db commit a311f05

2 files changed

Lines changed: 55 additions & 4 deletions

File tree

‎ext/intl/collator/collator_sort.c‎

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -255,12 +255,13 @@ static void collator_sort_internal( int renumber, INTERNAL_FUNCTION_PARAMETERS )
255255
UCollator* saved_collator;
256256
zval* array = NULL;
257257
HashTable* hash = NULL;
258+
zend_array* sorted = NULL;
258259
zend_long sort_flags = COLLATOR_SORT_REGULAR;
259260

260261
COLLATOR_METHOD_INIT_VARS
261262

262263
/* Parse parameters. */
263-
if( zend_parse_method_parameters( ZEND_NUM_ARGS(), getThis(), "Oa/|l",
264+
if( zend_parse_method_parameters( ZEND_NUM_ARGS(), getThis(), "Oa|l",
264265
&object, Collator_ce_ptr, &array, &sort_flags ) == FAILURE )
265266
{
266267
RETURN_THROWS();
@@ -279,24 +280,38 @@ static void collator_sort_internal( int renumber, INTERNAL_FUNCTION_PARAMETERS )
279280

280281
hash = Z_ARRVAL_P( array );
281282

283+
/* Copy array, so the in-place modifications will not be visible to the callback function */
284+
sorted = zend_array_dup( hash );
285+
282286
/* Convert strings in the specified array from UTF-8 to UTF-16. */
283-
collator_convert_hash_from_utf8_to_utf16( hash, COLLATOR_ERROR_CODE_P( co ) );
287+
collator_convert_hash_from_utf8_to_utf16( sorted, COLLATOR_ERROR_CODE_P( co ) );
288+
if( U_FAILURE( COLLATOR_ERROR_CODE( co ) ) ) {
289+
zend_array_destroy( sorted );
290+
}
284291
COLLATOR_CHECK_STATUS( co, "Error converting hash from UTF-8 to UTF-16" );
285292

286293
/* Save specified collator in the request-global (?) variable. */
287294
saved_collator = INTL_G( current_collator );
288295
INTL_G( current_collator ) = co->ucoll;
289296

290297
/* Sort specified array. */
291-
zend_hash_sort(hash, collator_compare_func, renumber);
298+
zend_hash_sort( sorted, collator_compare_func, renumber );
292299

293300
/* Restore saved collator. */
294301
INTL_G( current_collator ) = saved_collator;
295302

296303
/* Convert strings in the specified array back to UTF-8. */
297-
collator_convert_hash_from_utf16_to_utf8( hash, COLLATOR_ERROR_CODE_P( co ) );
304+
collator_convert_hash_from_utf16_to_utf8( sorted, COLLATOR_ERROR_CODE_P( co ) );
305+
if( U_FAILURE( COLLATOR_ERROR_CODE( co ) ) ) {
306+
zend_array_destroy( sorted );
307+
}
298308
COLLATOR_CHECK_STATUS( co, "Error converting hash from UTF-16 to UTF-8" );
299309

310+
zval garbage;
311+
ZVAL_COPY_VALUE( &garbage, array );
312+
ZVAL_ARR( array, sorted );
313+
zval_ptr_dtor( &garbage );
314+
300315
RETURN_TRUE;
301316
}
302317
/* }}} */
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
--TEST--
2+
Collator::sort(): mutating the array from __toString() during comparison must not corrupt the sort
3+
--EXTENSIONS--
4+
intl
5+
--FILE--
6+
<?php
7+
$c = new Collator('en_US');
8+
9+
class Grow {
10+
public static array $ref;
11+
public function __toString(): string {
12+
for ($i = 0; $i < 2000; $i++) {
13+
self::$ref[] = "x$i";
14+
}
15+
return "m";
16+
}
17+
}
18+
19+
$arr = ["z", new Grow(), "a", "b"];
20+
Grow::$ref = &$arr;
21+
var_dump($c->sort($arr));
22+
var_dump($arr);
23+
?>
24+
--EXPECT--
25+
bool(true)
26+
array(4) {
27+
[0]=>
28+
string(1) "a"
29+
[1]=>
30+
string(1) "b"
31+
[2]=>
32+
object(Grow)#2 (0) {
33+
}
34+
[3]=>
35+
string(1) "z"
36+
}

0 commit comments

Comments
 (0)