Skip to content

Commit 5c78980

Browse files
committed
Fix GH-11406: segfault with unpacking and magic method closure
The magic method trampoline closure may be variadic. However, the arg_info for the variadic argument was not set, resulting in a crash both in reflection and in the VM. Fix it by creating an arg_info containing a single element in case of the variadic case. The variadic argument is the last one (and in this case only one) in the arg_info array. We make sure the argument info is equivalent to the argument info of `$closure` of the following code snippet: ``` function foo(...$arguments) {} $closure = foo(...); ``` Closes GH-11417.
1 parent 18f2f0a commit 5c78980

File tree

3 files changed

+43
-0
lines changed

3 files changed

+43
-0
lines changed

NEWS

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ PHP NEWS
55
- Core:
66
. Fix GH-11388 (Allow "final" modifier when importing a method from a trait).
77
(nielsdos)
8+
. Fixed bug GH-11406 (segfault with unpacking and magic method closure).
9+
(nielsdos)
810

911
- DOM:
1012
. Fix #79700 (wrong use of libxml oldNs leads to performance problem).

Zend/tests/trampoline_closure_named_arguments.phpt

+35
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,32 @@ class Test {
1414

1515
$test = new Test;
1616

17+
$array = ["unpacked"];
18+
1719
echo "-- Non-static cases --\n";
1820
$test->test(1, 2, a: 123);
1921
$test->test(...)(1, 2);
2022
$test->test(...)(1, 2, a: 123, b: $test);
2123
$test->test(...)(a: 123, b: $test);
2224
$test->test(...)();
25+
$test->test(...)(...$array);
2326

2427
echo "-- Static cases --\n";
2528
Test::testStatic(1, 2, a: 123);
2629
Test::testStatic(...)(1, 2);
2730
Test::testStatic(...)(1, 2, a: 123, b: $test);
2831
Test::testStatic(...)(a: 123, b: $test);
2932
Test::testStatic(...)();
33+
Test::testStatic(...)(...$array);
34+
35+
echo "-- Reflection tests --\n";
36+
$reflectionFunction = new ReflectionFunction(Test::fail(...));
37+
var_dump($reflectionFunction->getParameters());
38+
$argument = $reflectionFunction->getParameters()[0];
39+
var_dump($argument->isVariadic());
40+
$type = $argument->getType();
41+
var_dump($type);
42+
var_dump($type->getName());
3043

3144
?>
3245
--EXPECT--
@@ -70,6 +83,11 @@ array(2) {
7083
string(4) "test"
7184
array(0) {
7285
}
86+
string(4) "test"
87+
array(1) {
88+
[0]=>
89+
string(8) "unpacked"
90+
}
7391
-- Static cases --
7492
string(10) "testStatic"
7593
array(3) {
@@ -110,3 +128,20 @@ array(2) {
110128
string(10) "testStatic"
111129
array(0) {
112130
}
131+
string(10) "testStatic"
132+
array(1) {
133+
[0]=>
134+
string(8) "unpacked"
135+
}
136+
-- Reflection tests --
137+
array(1) {
138+
[0]=>
139+
object(ReflectionParameter)#4 (1) {
140+
["name"]=>
141+
string(9) "arguments"
142+
}
143+
}
144+
bool(true)
145+
object(ReflectionNamedType)#5 (0) {
146+
}
147+
string(5) "mixed"

Zend/zend_closures.c

+6
Original file line numberDiff line numberDiff line change
@@ -833,6 +833,9 @@ ZEND_API void zend_create_fake_closure(zval *res, zend_function *func, zend_clas
833833
}
834834
/* }}} */
835835

836+
/* __call and __callStatic name the arguments "$arguments" in the docs. */
837+
static zend_internal_arg_info trampoline_arg_info[] = {ZEND_ARG_VARIADIC_TYPE_INFO(false, arguments, IS_MIXED, false)};
838+
836839
void zend_closure_from_frame(zval *return_value, zend_execute_data *call) { /* {{{ */
837840
zval instance;
838841
zend_internal_function trampoline;
@@ -856,6 +859,9 @@ void zend_closure_from_frame(zval *return_value, zend_execute_data *call) { /* {
856859
trampoline.handler = zend_closure_call_magic;
857860
trampoline.function_name = mptr->common.function_name;
858861
trampoline.scope = mptr->common.scope;
862+
if (trampoline.fn_flags & ZEND_ACC_VARIADIC) {
863+
trampoline.arg_info = trampoline_arg_info;
864+
}
859865

860866
zend_free_trampoline(mptr);
861867
mptr = (zend_function *) &trampoline;

0 commit comments

Comments
 (0)