From c68afeed16a6a17de2415ddfecae18484b2a6c25 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Tue, 16 Sep 2025 13:39:35 +0200 Subject: [PATCH 1/3] Fix use-of-uninitialized-value in zend_get_arg_offset_by_name() Don't access fbc->op_array.refcount on internal function. Don't attempt to cache ZEND_ACC_USER_ARG_INFO at all, which is only used in zend_get_closure_invoke_method(). This may reuse arg_info from a temporary closure, and hence caching would also be unsafe. --- Zend/zend_execute.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 4e6339ca901c..eff7306c19d2 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -5072,9 +5072,9 @@ static zend_always_inline uint32_t zend_get_arg_offset_by_name( if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) || EXPECTED(fbc->common.fn_flags & ZEND_ACC_USER_ARG_INFO)) { for (uint32_t i = 0; i < num_args; i++) { - zend_arg_info *arg_info = &fbc->op_array.arg_info[i]; + zend_arg_info *arg_info = &fbc->common.arg_info[i]; if (zend_string_equals(arg_name, arg_info->name)) { - if (!fbc->op_array.refcount || !(fbc->op_array.fn_flags & ZEND_ACC_CLOSURE)) { + if (fbc->type == ZEND_USER_FUNCTION && (!fbc->op_array.refcount || !(fbc->op_array.fn_flags & ZEND_ACC_CLOSURE))) { *cache_slot = unique_id; *(uintptr_t *)(cache_slot + 1) = i; } From d5d29a6649de53bc6b163637a15b08943f51b618 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Wed, 17 Sep 2025 13:30:57 +0200 Subject: [PATCH 2/3] Guard variadic parameter of temporary closure from populating cache slot This may happen when a closure with a variadic parameter is called with $closure->__invoke(). --- Zend/zend_execute.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index eff7306c19d2..b95cbc2e185b 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -5094,7 +5094,10 @@ static zend_always_inline uint32_t zend_get_arg_offset_by_name( } if (fbc->common.fn_flags & ZEND_ACC_VARIADIC) { - if (fbc->type == ZEND_INTERNAL_FUNCTION || !fbc->op_array.refcount || !(fbc->op_array.fn_flags & ZEND_ACC_CLOSURE)) { + if (!(fbc->common.fn_flags & ZEND_ACC_USER_ARG_INFO) + && (fbc->type == ZEND_INTERNAL_FUNCTION + || !fbc->op_array.refcount + || !(fbc->op_array.fn_flags & ZEND_ACC_CLOSURE))) { *cache_slot = unique_id; *(uintptr_t *)(cache_slot + 1) = fbc->common.num_args; } From e2b59ff6f6ecb9ecb3413a210d359ab7f4e31d53 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Wed, 17 Sep 2025 14:06:45 +0200 Subject: [PATCH 3/3] Fix ZEND_ACC_USER_ARG_INFO, whose value is reused for user functions --- Zend/zend_execute.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index b95cbc2e185b..d44af38c64c6 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -5094,10 +5094,10 @@ static zend_always_inline uint32_t zend_get_arg_offset_by_name( } if (fbc->common.fn_flags & ZEND_ACC_VARIADIC) { - if (!(fbc->common.fn_flags & ZEND_ACC_USER_ARG_INFO) - && (fbc->type == ZEND_INTERNAL_FUNCTION - || !fbc->op_array.refcount - || !(fbc->op_array.fn_flags & ZEND_ACC_CLOSURE))) { + if ((fbc->type == ZEND_USER_FUNCTION + && (!fbc->op_array.refcount || !(fbc->op_array.fn_flags & ZEND_ACC_CLOSURE))) + || (fbc->type == ZEND_INTERNAL_FUNCTION + && !(fbc->common.fn_flags & ZEND_ACC_USER_ARG_INFO))) { *cache_slot = unique_id; *(uintptr_t *)(cache_slot + 1) = fbc->common.num_args; }