Merge branch 'PHP-8.3' into PHP-8.4

* PHP-8.3:
  Fix GH-19300: Nested array_multisort invocation with error breaks
This commit is contained in:
Niels Dossche 2025-07-31 19:01:13 +02:00
commit b82c8ba7fe
No known key found for this signature in database
GPG key ID: B8A8AD166DF0E2E5
4 changed files with 90 additions and 4 deletions

2
NEWS
View file

@ -67,6 +67,8 @@ PHP NEWS
. Fixed OSS Fuzz #433303828 (Leak in failed unserialize() with opcache).
(ilutov)
. Fix theoretical issues with hrtime() not being available. (nielsdos)
. Fixed bug GH-19300 (Nested array_multisort invocation with error breaks).
(nielsdos)
- Windows:
. Free opened_path when opened_path_len >= MAXPATHLEN. (dixyes)

View file

@ -6038,7 +6038,7 @@ PHP_FUNCTION(array_multisort)
for (i = 0; i < MULTISORT_LAST; i++) {
parse_state[i] = 0;
}
func = ARRAYG(multisort_func) = ecalloc(argc, sizeof(bucket_compare_func_t));
func = ecalloc(argc, sizeof(bucket_compare_func_t));
/* Here we go through the input arguments and parse them. Each one can
* be either an array or a sort flag which follows an array. If not
@ -6054,7 +6054,7 @@ PHP_FUNCTION(array_multisort)
/* We see the next array, so we update the sort flags of
* the previous array and reset the sort flags. */
if (i > 0) {
ARRAYG(multisort_func)[num_arrays - 1] = php_get_data_compare_func_unstable(sort_type, sort_order != PHP_SORT_ASC);
func[num_arrays - 1] = php_get_data_compare_func_unstable(sort_type, sort_order != PHP_SORT_ASC);
sort_order = PHP_SORT_ASC;
sort_type = PHP_SORT_REGULAR;
}
@ -6106,8 +6106,6 @@ PHP_FUNCTION(array_multisort)
MULTISORT_ABORT;
}
}
/* Take care of the last array sort flags. */
ARRAYG(multisort_func)[num_arrays - 1] = php_get_data_compare_func_unstable(sort_type, sort_order != PHP_SORT_ASC);
/* Make sure the arrays are of the same size. */
array_size = zend_hash_num_elements(Z_ARRVAL_P(arrays[0]));
@ -6125,6 +6123,11 @@ PHP_FUNCTION(array_multisort)
RETURN_TRUE;
}
/* Take care of the last array sort flags. */
func[num_arrays - 1] = php_get_data_compare_func_unstable(sort_type, sort_order != PHP_SORT_ASC);
bucket_compare_func_t *old_multisort_func = ARRAYG(multisort_func);
ARRAYG(multisort_func) = func;
/* Create the indirection array. This array is of size MxN, where
* M is the number of entries in each input array and N is the number
* of the input arrays + 1. The last column is UNDEF to indicate the end
@ -6201,6 +6204,7 @@ clean_up:
efree(indirect);
efree(func);
efree(arrays);
ARRAYG(multisort_func) = old_multisort_func;
}
/* }}} */

View file

@ -0,0 +1,40 @@
--TEST--
GH-19300 (Nested array_multisort invocation with error breaks) - correct invocation variation
--FILE--
<?php
class MyStringable {
public function __construct(private string $data) {}
public function __tostring() {
array_multisort([]); // Trigger update of array sort globals in happy path
return $this->data;
}
}
$inputs = [
new MyStringable('3'),
new MyStringable('1'),
new MyStringable('2'),
];
var_dump(array_multisort($inputs, SORT_STRING));
var_dump($inputs);
?>
--EXPECT--
bool(true)
array(3) {
[0]=>
object(MyStringable)#2 (1) {
["data":"MyStringable":private]=>
string(1) "1"
}
[1]=>
object(MyStringable)#3 (1) {
["data":"MyStringable":private]=>
string(1) "2"
}
[2]=>
object(MyStringable)#1 (1) {
["data":"MyStringable":private]=>
string(1) "3"
}
}

View file

@ -0,0 +1,40 @@
--TEST--
GH-19300 (Nested array_multisort invocation with error breaks) - error variation
--FILE--
<?php
function error_handle($level, $message, $file = '', $line = 0){
try {
array_multisort($a, SORT_ASC); // Trigger multisort abort
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}
}
set_error_handler('error_handle');
$inputs = [
new stdClass,
new stdClass,
new stdClass,
];
var_dump(array_multisort($inputs, SORT_NUMERIC));
var_dump($inputs);
?>
--EXPECT--
array_multisort(): Argument #1 ($array) must be an array or a sort flag
array_multisort(): Argument #1 ($array) must be an array or a sort flag
array_multisort(): Argument #1 ($array) must be an array or a sort flag
array_multisort(): Argument #1 ($array) must be an array or a sort flag
bool(true)
array(3) {
[0]=>
object(stdClass)#1 (0) {
}
[1]=>
object(stdClass)#2 (0) {
}
[2]=>
object(stdClass)#3 (0) {
}
}