Skip to content

Commit 9ccfb2f

Browse files
committed
Fix caching arg by name
Non-persistent arg infos allocated by pdo_hash_methods() break zend_get_arg_offset_by_name() again. Fix zend_get_arg_offset_by_name() by excluding ZEND_ACC_NEVER_CACHE instead of ZEND_ACC_USER_ARG_INFO. Also flag Closure::__invoke() with ZEND_ACC_NEVER_CACHE (It was already flagged with ZEND_ACC_CALL_VIA_HANDLER, which is synonymous of ZEND_ACC_NEVER_CACHE WRT caching). This would allow to remove ZEND_ACC_USER_ARG_INFO later.
1 parent 76622eb commit 9ccfb2f

File tree

3 files changed

+24
-3
lines changed

3 files changed

+24
-3
lines changed

Zend/tests/closures/gh19653_3.phpt

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

Zend/zend_closures.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -497,7 +497,7 @@ ZEND_API zend_function *zend_get_closure_invoke_method(zend_object *object) /* {
497497
* ZEND_ACC_USER_ARG_INFO flag to prevent invalid usage by Reflection */
498498
invoke->type = ZEND_INTERNAL_FUNCTION;
499499
invoke->internal_function.fn_flags =
500-
ZEND_ACC_PUBLIC | ZEND_ACC_CALL_VIA_HANDLER | (closure->func.common.fn_flags & keep_flags);
500+
ZEND_ACC_PUBLIC | ZEND_ACC_CALL_VIA_HANDLER | ZEND_ACC_NEVER_CACHE | (closure->func.common.fn_flags & keep_flags);
501501
if (closure->func.type != ZEND_INTERNAL_FUNCTION || (closure->func.common.fn_flags & ZEND_ACC_USER_ARG_INFO)) {
502502
invoke->internal_function.fn_flags |=
503503
ZEND_ACC_USER_ARG_INFO;

Zend/zend_execute.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5478,7 +5478,7 @@ static zend_always_inline uint32_t zend_get_arg_offset_by_name(
54785478
if ((fbc->type == ZEND_USER_FUNCTION
54795479
&& (!fbc->op_array.refcount || !(fbc->op_array.fn_flags & ZEND_ACC_CLOSURE)))
54805480
|| (fbc->type == ZEND_INTERNAL_FUNCTION
5481-
&& !(fbc->common.fn_flags & ZEND_ACC_USER_ARG_INFO))) {
5481+
&& !(fbc->common.fn_flags & ZEND_ACC_NEVER_CACHE))) {
54825482
*cache_slot = unique_id;
54835483
*(uintptr_t *)(cache_slot + 1) = i;
54845484
}
@@ -5490,7 +5490,7 @@ static zend_always_inline uint32_t zend_get_arg_offset_by_name(
54905490
if ((fbc->type == ZEND_USER_FUNCTION
54915491
&& (!fbc->op_array.refcount || !(fbc->op_array.fn_flags & ZEND_ACC_CLOSURE)))
54925492
|| (fbc->type == ZEND_INTERNAL_FUNCTION
5493-
&& !(fbc->common.fn_flags & ZEND_ACC_USER_ARG_INFO))) {
5493+
&& !(fbc->common.fn_flags & ZEND_ACC_NEVER_CACHE))) {
54945494
*cache_slot = unique_id;
54955495
*(uintptr_t *)(cache_slot + 1) = fbc->common.num_args;
54965496
}

0 commit comments

Comments
 (0)