mirror of
https://github.com/php/php-src.git
synced 2025-08-15 13:38:49 +02:00
Fix GH-19300: Nested array_multisort invocation with error breaks
There are 2 issues: 1. When a MULTISORT_ABORT happens, it frees func, but func may point to ARRAYG(multisort_func), which would be a problem with nested invocations as it can destroy that of the "parent" invocation. To solve this, delay assigning to the globals. 2. The old globals were not restored which means that nested invocations with different flags will cause a wrong sorting function to be used. Closes GH-19319.
This commit is contained in:
parent
6fa8a25a40
commit
a96b05e63f
4 changed files with 90 additions and 4 deletions
2
NEWS
2
NEWS
|
@ -68,6 +68,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)
|
||||
|
|
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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]));
|
||||
|
@ -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
|
||||
|
@ -6074,6 +6077,7 @@ clean_up:
|
|||
efree(indirect);
|
||||
efree(func);
|
||||
efree(arrays);
|
||||
ARRAYG(multisort_func) = old_multisort_func;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
|
|
40
ext/standard/tests/array/gh19300_1.phpt
Normal file
40
ext/standard/tests/array/gh19300_1.phpt
Normal 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"
|
||||
}
|
||||
}
|
40
ext/standard/tests/array/gh19300_2.phpt
Normal file
40
ext/standard/tests/array/gh19300_2.phpt
Normal 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) {
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue