Skip to content

Commit b82c8ba

Browse files
committed
Merge branch 'PHP-8.3' into PHP-8.4
* PHP-8.3: Fix GH-19300: Nested array_multisort invocation with error breaks
2 parents 781d77a + a96b05e commit b82c8ba

File tree

4 files changed

+90
-4
lines changed

4 files changed

+90
-4
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ PHP NEWS
6767
. Fixed OSS Fuzz #433303828 (Leak in failed unserialize() with opcache).
6868
(ilutov)
6969
. Fix theoretical issues with hrtime() not being available. (nielsdos)
70+
. Fixed bug GH-19300 (Nested array_multisort invocation with error breaks).
71+
(nielsdos)
7072

7173
- Windows:
7274
. Free opened_path when opened_path_len >= MAXPATHLEN. (dixyes)

ext/standard/array.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6038,7 +6038,7 @@ PHP_FUNCTION(array_multisort)
60386038
for (i = 0; i < MULTISORT_LAST; i++) {
60396039
parse_state[i] = 0;
60406040
}
6041-
func = ARRAYG(multisort_func) = ecalloc(argc, sizeof(bucket_compare_func_t));
6041+
func = ecalloc(argc, sizeof(bucket_compare_func_t));
60426042

60436043
/* Here we go through the input arguments and parse them. Each one can
60446044
* be either an array or a sort flag which follows an array. If not
@@ -6054,7 +6054,7 @@ PHP_FUNCTION(array_multisort)
60546054
/* We see the next array, so we update the sort flags of
60556055
* the previous array and reset the sort flags. */
60566056
if (i > 0) {
6057-
ARRAYG(multisort_func)[num_arrays - 1] = php_get_data_compare_func_unstable(sort_type, sort_order != PHP_SORT_ASC);
6057+
func[num_arrays - 1] = php_get_data_compare_func_unstable(sort_type, sort_order != PHP_SORT_ASC);
60586058
sort_order = PHP_SORT_ASC;
60596059
sort_type = PHP_SORT_REGULAR;
60606060
}
@@ -6106,8 +6106,6 @@ PHP_FUNCTION(array_multisort)
61066106
MULTISORT_ABORT;
61076107
}
61086108
}
6109-
/* Take care of the last array sort flags. */
6110-
ARRAYG(multisort_func)[num_arrays - 1] = php_get_data_compare_func_unstable(sort_type, sort_order != PHP_SORT_ASC);
61116109

61126110
/* Make sure the arrays are of the same size. */
61136111
array_size = zend_hash_num_elements(Z_ARRVAL_P(arrays[0]));
@@ -6125,6 +6123,11 @@ PHP_FUNCTION(array_multisort)
61256123
RETURN_TRUE;
61266124
}
61276125

6126+
/* Take care of the last array sort flags. */
6127+
func[num_arrays - 1] = php_get_data_compare_func_unstable(sort_type, sort_order != PHP_SORT_ASC);
6128+
bucket_compare_func_t *old_multisort_func = ARRAYG(multisort_func);
6129+
ARRAYG(multisort_func) = func;
6130+
61286131
/* Create the indirection array. This array is of size MxN, where
61296132
* M is the number of entries in each input array and N is the number
61306133
* of the input arrays + 1. The last column is UNDEF to indicate the end
@@ -6201,6 +6204,7 @@ PHP_FUNCTION(array_multisort)
62016204
efree(indirect);
62026205
efree(func);
62036206
efree(arrays);
6207+
ARRAYG(multisort_func) = old_multisort_func;
62046208
}
62056209
/* }}} */
62066210

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
--TEST--
2+
GH-19300 (Nested array_multisort invocation with error breaks) - correct invocation variation
3+
--FILE--
4+
<?php
5+
class MyStringable {
6+
public function __construct(private string $data) {}
7+
public function __tostring() {
8+
array_multisort([]); // Trigger update of array sort globals in happy path
9+
return $this->data;
10+
}
11+
}
12+
13+
$inputs = [
14+
new MyStringable('3'),
15+
new MyStringable('1'),
16+
new MyStringable('2'),
17+
];
18+
19+
var_dump(array_multisort($inputs, SORT_STRING));
20+
var_dump($inputs);
21+
?>
22+
--EXPECT--
23+
bool(true)
24+
array(3) {
25+
[0]=>
26+
object(MyStringable)#2 (1) {
27+
["data":"MyStringable":private]=>
28+
string(1) "1"
29+
}
30+
[1]=>
31+
object(MyStringable)#3 (1) {
32+
["data":"MyStringable":private]=>
33+
string(1) "2"
34+
}
35+
[2]=>
36+
object(MyStringable)#1 (1) {
37+
["data":"MyStringable":private]=>
38+
string(1) "3"
39+
}
40+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
--TEST--
2+
GH-19300 (Nested array_multisort invocation with error breaks) - error variation
3+
--FILE--
4+
<?php
5+
6+
function error_handle($level, $message, $file = '', $line = 0){
7+
try {
8+
array_multisort($a, SORT_ASC); // Trigger multisort abort
9+
} catch (TypeError $e) {
10+
echo $e->getMessage(), "\n";
11+
}
12+
}
13+
set_error_handler('error_handle');
14+
15+
$inputs = [
16+
new stdClass,
17+
new stdClass,
18+
new stdClass,
19+
];
20+
21+
var_dump(array_multisort($inputs, SORT_NUMERIC));
22+
var_dump($inputs);
23+
?>
24+
--EXPECT--
25+
array_multisort(): Argument #1 ($array) must be an array or a sort flag
26+
array_multisort(): Argument #1 ($array) must be an array or a sort flag
27+
array_multisort(): Argument #1 ($array) must be an array or a sort flag
28+
array_multisort(): Argument #1 ($array) must be an array or a sort flag
29+
bool(true)
30+
array(3) {
31+
[0]=>
32+
object(stdClass)#1 (0) {
33+
}
34+
[1]=>
35+
object(stdClass)#2 (0) {
36+
}
37+
[2]=>
38+
object(stdClass)#3 (0) {
39+
}
40+
}

0 commit comments

Comments
 (0)