Skip to content

Commit d6300a3

Browse files
committed
Merge branch 'PHP-8.4'
* PHP-8.4: Fix GH-19653: Closure named argument unpacking between temporary closures can cause a crash
2 parents 02c13b6 + a3d27aa commit d6300a3

File tree

3 files changed

+65
-6
lines changed

3 files changed

+65
-6
lines changed

Zend/tests/closures/gh19653_1.phpt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
--TEST--
2+
GH-19653 (Closure named argument unpacking between temporary closures can cause a crash)
3+
--CREDITS--
4+
ivan-u7n
5+
--FILE--
6+
<?php
7+
8+
function func1(string $a1 = 'a1', string $a2 = 'a2', string $a3 = 'a3') {
9+
echo __FUNCTION__ . "() a1=$a1 a2=$a2 a3=$a3\n";
10+
}
11+
12+
function usage1(?Closure $func1 = null) {
13+
echo __FUNCTION__ . "() ";
14+
($func1 ?? func1(...))(a3: 'm3+');
15+
}
16+
usage1();
17+
18+
$func1 = function (string ...$args) {
19+
echo "[function] ";
20+
func1(...$args, a2: 'm2+');
21+
};
22+
usage1(func1: $func1);
23+
24+
?>
25+
--EXPECT--
26+
usage1() func1() a1=a1 a2=a2 a3=m3+
27+
usage1() [function] func1() a1=a1 a2=m2+ a3=m3+

Zend/tests/closures/gh19653_2.phpt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
--TEST--
2+
GH-19653 (Closure named argument unpacking between temporary closures can cause a crash) - eval variation
3+
--CREDITS--
4+
arnaud-lb
5+
--FILE--
6+
<?php
7+
8+
function usage1(Closure $c) {
9+
$c(a: 1);
10+
}
11+
12+
usage1(eval('return function($a) { var_dump($a); };'));
13+
usage1(eval('return function($b) { var_dump($b); };'));
14+
15+
?>
16+
--EXPECTF--
17+
int(1)
18+
19+
Fatal error: Uncaught Error: Unknown named parameter $a in %s:%d
20+
Stack trace:
21+
#0 %s(%d): usage1(Object(Closure))
22+
#1 {main}
23+
thrown in %s on line %d

Zend/zend_execute.c

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5440,7 +5440,12 @@ static zend_never_inline zend_result ZEND_FASTCALL zend_quick_check_constant(
54405440

54415441
static zend_always_inline uint32_t zend_get_arg_offset_by_name(
54425442
zend_function *fbc, zend_string *arg_name, void **cache_slot) {
5443-
if (EXPECTED(*cache_slot == fbc)) {
5443+
/* Due to closures, the `fbc` address isn't unique if the memory address is reused.
5444+
* The argument info will be however and uniquely positions the arguments.
5445+
* We do support NULL arg_info, so we have to distinguish that from an uninitialized cache slot. */
5446+
void *unique_id = (void *) ((uintptr_t) fbc->common.arg_info | 1);
5447+
5448+
if (EXPECTED(*cache_slot == unique_id)) {
54445449
return *(uintptr_t *)(cache_slot + 1);
54455450
}
54465451

@@ -5451,8 +5456,10 @@ static zend_always_inline uint32_t zend_get_arg_offset_by_name(
54515456
for (uint32_t i = 0; i < num_args; i++) {
54525457
zend_arg_info *arg_info = &fbc->op_array.arg_info[i];
54535458
if (zend_string_equals(arg_name, arg_info->name)) {
5454-
*cache_slot = fbc;
5455-
*(uintptr_t *)(cache_slot + 1) = i;
5459+
if (!fbc->op_array.refcount || !(fbc->op_array.fn_flags & ZEND_ACC_CLOSURE)) {
5460+
*cache_slot = unique_id;
5461+
*(uintptr_t *)(cache_slot + 1) = i;
5462+
}
54565463
return i;
54575464
}
54585465
}
@@ -5462,16 +5469,18 @@ static zend_always_inline uint32_t zend_get_arg_offset_by_name(
54625469
zend_internal_arg_info *arg_info = &fbc->internal_function.arg_info[i];
54635470
size_t len = strlen(arg_info->name);
54645471
if (zend_string_equals_cstr(arg_name, arg_info->name, len)) {
5465-
*cache_slot = fbc;
5472+
*cache_slot = unique_id;
54665473
*(uintptr_t *)(cache_slot + 1) = i;
54675474
return i;
54685475
}
54695476
}
54705477
}
54715478

54725479
if (fbc->common.fn_flags & ZEND_ACC_VARIADIC) {
5473-
*cache_slot = fbc;
5474-
*(uintptr_t *)(cache_slot + 1) = fbc->common.num_args;
5480+
if (fbc->type == ZEND_INTERNAL_FUNCTION || !fbc->op_array.refcount || !(fbc->op_array.fn_flags & ZEND_ACC_CLOSURE)) {
5481+
*cache_slot = unique_id;
5482+
*(uintptr_t *)(cache_slot + 1) = fbc->common.num_args;
5483+
}
54755484
return fbc->common.num_args;
54765485
}
54775486

0 commit comments

Comments
 (0)