Skip to content

Fix GH-19300: Nested array_multisort invocations break #19319

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions ext/standard/array.c
Original file line number Diff line number Diff line change
Expand Up @@ -5911,7 +5911,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
Expand All @@ -5927,7 +5927,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;
}
Expand Down Expand Up @@ -5979,8 +5979,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]));
Expand All @@ -5998,6 +5996,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
Expand Down Expand Up @@ -6074,6 +6077,7 @@ PHP_FUNCTION(array_multisort)
efree(indirect);
efree(func);
efree(arrays);
ARRAYG(multisort_func) = old_multisort_func;
}
/* }}} */

Expand Down
40 changes: 40 additions & 0 deletions ext/standard/tests/array/gh19300_1.phpt
Original file line number Diff line number Diff line change
@@ -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"
}
}
40 changes: 40 additions & 0 deletions ext/standard/tests/array/gh19300_2.phpt
Original file line number Diff line number Diff line change
@@ -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) {
}
}
Loading